16

What should I do to be able to use #oauth2 security expressions on method level like on the example below?

@RequestMapping(value = "email", method = RequestMethod.GET)
  @ResponseBody
  @PreAuthorize("#oauth2.hasScope('read')")
  public String email() {

    return "test@email.com";
  }

If I do a request to that resource I receive

    [INFO] java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.hasScope('read')'
[INFO]  at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:14)
[INFO]  at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:44)
[INFO]  at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:57)
[INFO]  at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:25)
[INFO]  at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:62)
[INFO]  at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232)
[INFO]  at org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor.invoke(AspectJMethodSecurityInterceptor.java:43)
[INFO]  at org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect.ajc$around$org_springframework_security_access_intercept_aspectj_aspect_AnnotationSecurityAspect$1$c4d57a2b(AnnotationSecurityAspect.aj:63)
[INFO]  at pl.insert.controllers.ResourceController.email(ResourceController.java:22)

The same thing works well if I specify the access in my ResourceServerConfiguration instead of @Controllers' methods

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http.requestMatchers().antMatchers("/oauth/resources/**");
    http.authorizeRequests().anyRequest().access("#oauth2.hasScope('read')");
  }
}

Standard security expressions like @PreAuthorize("permitAll") or @PreAuthorize("denyAll") work as expected. So, probably I have to tell somehow to my AspectJMethodSecurityInterceptor to use OAuth2WebSecurityExpressionHandler. Any ideas?

Marek Raki
  • 3,056
  • 3
  • 27
  • 50

6 Answers6

16

To enable #oAuth2 security expressions it is only needed to set the default expression handler as OAuth2MethodSecurityExpressionHandler instead of DefaultMethodSecurityExpressionHandler. Because OAuth2MethodSecurityExpressionHandler extends it anyway, then the whole previous functionality remains the same. In my configuration I use both GlobalMethodSecurityConfiguration and WebSecurityConfigurerAdapter.

@Configuration
@EnableGlobalMethodSecurity
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

  @Override
  protected MethodSecurityExpressionHandler createExpressionHandler() {
    return new OAuth2MethodSecurityExpressionHandler();
  }
}

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  ...
}


@Configuration
@Import({ SecurityConfiguration.class, MethodSecurityConfiguration.class })
public class AppConfiguration {
  ...
}
kc2001
  • 5,008
  • 4
  • 51
  • 92
Marek Raki
  • 3,056
  • 3
  • 27
  • 50
  • I have the same issue. Tried this, but it didn't help. Could someone please share some source code? – Ben Mar 27 '16 at 23:03
  • 2
    What does it mean 'it didn't help'? Have the @Configuration class been loaded and createExpressionHandler() method ever been called? If 'yes' check in a debugger if your AspectJMethodSecurityInterceptor really uses OAuth2MethodSecurityExpressionHandler. If 'no' investigate why. – Marek Raki Mar 27 '16 at 23:59
  • Adding the override to `createExpressionHandler` works fine for me. The other solutions that claim that you can just add the `spring-security-oauth2-autoconfigure` dependency do not work for me (At least no in my unit test, did not check my application itself). – Wim Deblauwe Jul 26 '19 at 15:45
5

I think you also need to add: @EnableGlobalMethodSecurity(prePostEnabled = true) in order to get it to work.

Answered on deferent page

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
thygesen
  • 51
  • 1
  • 1
5

A simpler solution would be to let Spring Boot autoconfigure. Adding the following dependency solved this for me:

compile('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.4.RELEASE')
JohanB
  • 2,068
  • 1
  • 15
  • 15
  • I had to delete the configuration related to GlobalMethodSecurityConfiguration class and I added this dependency then it worked. – kelgwiin Jun 20 '19 at 21:53
2

This is an old question, things have changed. With Spring Security 5 one should use:

.hasAuthority("SCOPE_scopename")

Spring adds authorities to the principal based on the scopes it received from the provider, prefixed with “SCOPE_“.

More info: https://www.baeldung.com/spring-security-openid-connect

Janek Olszak
  • 4,067
  • 1
  • 28
  • 22
0

I had the same problem, but only in a unit test (@WebMvcTest). I had to add @EnableGlobalMethodSecurity on the inner class that defined the configuration for the test:

@RunWith(SpringRunner.class)
@WebMvcTest(MyController.class)
public class MyControllerTest {

  @TestConfiguration
  @Import({JacksonCustomizations.class,SecuritySettings.class,
        OAuth2ServerConfiguration.class, WebSecurityConfiguration.class,
        TokenGrantersConfiguration.class})
  @EnableGlobalMethodSecurity
  public static class TestConfig {
  }
}

UPDATE: In Spring Boot 2.x, you might get:

java.lang.IllegalStateException: In the composition of all global method configuration, no annotation support was actually activated

The reason is that you added @EnableGlobalMethodSecurity without actually enabling anything. To fix it, set at least one of the properties of the annotation to true. E.g:

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
Wim Deblauwe
  • 25,113
  • 20
  • 133
  • 211
0

For me, it was the combination of this answer

// spring configuration class annotation
@EnableGlobalMethodSecurity(prePostEnabled = true)

and this other answer

// gradle dependencuy
compile('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.4.RELEASE')
GreenGiant
  • 4,930
  • 1
  • 46
  • 76