I was trying to replace some manual authority checking with annotations (@Secured
and @PreAuthorize
). While debugging why it doesn't work I was surprised to find that the second of these two assertions failed at the top of a @RequestMapping
controller method.
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
assert(auth.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_READER"))); // passes
assert(request.isUserInRole("ROLE_READER")); // fails
I assume (as I can't get them to authorise anything) @Secured
and hasRole()
make use of the latter lookup?
Are the roles not supposed to be automatically populated from the SecurityContext
authorities?
Was the filter that set the Authentication
supposed to add the roles separately?
Edit:
Cut down the spring security config to spring boot's (1.3.0) default, plus the filter that sets the authentication.
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new JwtAuthenticationFilter(), FilterSecurityInterceptor.class);
}
}
I assume (as I can't get them to authorise anything) @Secured and hasRole() make use of the latter lookup?
My assumption was wrong. Both use the granted authorities, but in different ways.
@Secured
requires the prefix, so @Secured("ROLE_READER")
works.hasRole
does not use the prefix, so @PreAuthorize("hasRole('READER')")
works.@Secured("READER")
will never work, even if there is an authority named READER
.The prefix can be configured with RoleVoter
's rolePrefix
property.
HttpServletRequest.isUserInRole
uses a completely separate system, unrelated to Spring security. It is not populated by default, and does not need to be. I believe adding a SecurityContextHolderAwareRequestFilter
to the chain will populate it