I'm trying to rewrite a previous example with JWT's built with a custom JWT Filter into a simplified version based on Springs new authorization server and this example: https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/java/jwt/login
The example sets up an InMemoryUserDetailsManager with a single user → user,password and an "app" authority so I assume it is designed to handle roles/authorities?
Everything works fine (as explained in the examples README) if I use the provided SecurityFilterChain But if I change this:
...
http.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
Into this
...
http.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/").hasRole("app")
//.antMatchers("/").hasAuthority("app")
.anyRequest().authenticated()
)
I get a 403 Status back The authority gets added to the JWT as expected like this:
..
"scope": "app"
}
Apart from the antMatchers given above, my code is exactly as clone from the Spring Security example
What am I missing here?
CodePudding user response:
OK, read the specs ;-) Accoring to https://docs.spring.io/spring-security/reference/reactive/oauth2/resource-server/jwt.html Authorities gets prefixed with a SCOPE_
So this partly fixes the problem .antMatchers("/").hasAuthority("SCOPE_app")
I still havent figured out how to use hasRoles?
CodePudding user response:
To use hasRole
, you need to have authorities which start with ROLE_
. What you could do is register a converter which would read roles from JWT and add them as GrantedAuthority
.
public class RolesClaimConverter implements Converter<Jwt, AbstractAuthenticationToken> {
private final JwtGrantedAuthoritiesConverter wrappedConverter;
public RolesClaimConverter(JwtGrantedAuthoritiesConverter conv) {
wrappedConverter = conv;
}
@Override
public AbstractAuthenticationToken convert(@NonNull Jwt jwt) {
// get authorities from wrapped converter
var grantedAuthorities = new ArrayList<>(wrappedConverter.convert(jwt));
// get role authorities
var roles = (List<String>) jwt.getClaims().get("roles");
if (roles != null) {
for (String role : roles) {
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" role));
}
}
return new JwtAuthenticationToken(jwt, grantedAuthorities);
}
}
Then register your converter in your security configuration
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer(resourceServer -> resourceServer
.jwt()
.jwtAuthenticationConverter(new RolesClaimConverter(new JwtGrantedAuthoritiesConverter()))
)
// other configuration
;
return http.build();
}
And that's it. All you need now is to pass list of roles when creating JWT and you can use .antMatchers("/").hasRole("app")
and @PreAuthorize("hasRole('app')")
in your code.