Marek Raki
Marek Raki

Reputation: 3146

#oauth2 security expressions on method level

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 "[email protected]";
  }

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?

Upvotes: 16

Views: 19173

Answers (7)

Sumit Badge
Sumit Badge

Reputation: 58

In Spring 6 & Boot 3

@EnableMethodSecurity(securedEnabled = true)  // bootstrap class


// in controller 
@PreAuthorize("hasAuthority('SCOPE_write')")
@GetMapping("/api/users")
public List<?> getUsers() {
    System.err.println("API invoked in Resource Server");
    return of("john", "allen", "Ashley" );
}

Upvotes: 1

Marek Raki
Marek Raki

Reputation: 3146

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 {
  ...
}

Upvotes: 17

Janek Olszak
Janek Olszak

Reputation: 4323

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

Upvotes: 4

GreenGiant
GreenGiant

Reputation: 5256

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')

Upvotes: 0

JohanB
JohanB

Reputation: 2148

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')

Upvotes: 5

Wim Deblauwe
Wim Deblauwe

Reputation: 26878

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)

Upvotes: 0

thygesen
thygesen

Reputation: 51

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

Answered on deferent page

Upvotes: 5

Related Questions