Reputation: 3146
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
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
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
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
Reputation: 5256
For me, it was the combination of this answer
// spring configuration class annotation
@EnableGlobalMethodSecurity(prePostEnabled = true)
// gradle dependencuy
compile('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.4.RELEASE')
Upvotes: 0
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
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
Reputation: 51
I think you also need to add: @EnableGlobalMethodSecurity(prePostEnabled = true) in order to get it to work.
Upvotes: 5