Sometimes, JWT tokens contain custom claims. And sometimes, those custom claims contain the user roles, instead of the default scope
claim. What if we could teach Spring Security about these fields and map them to the Principal object? Here’s how.
To inspect the currently logged in user in Spring Security you can simply autowire the Principal
object into a controller method as follows:
@GetMapping("/test")
public void test(Principal principal) {
return principal.toString();
}
By default, Spring Security maps the scope
or scp
claims from the JWT token to the authorities
field of the JWTAuthenticationToken
instance, which will populate the Principal
field. These claims will be prefixed by SCOPE_
, enabling you to use for instance @PreAuthorize("hasAuthority('SCOPE_admin')"
on a controller method.
But what if your user roles are in custom JWT claims? No sweat, because this is actually relatively simple to configure in your SecurityConfig
class:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter());
}
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter =
new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtAuthenticationConverter =
new JwtAuthenticationConverter();
jwtAuthenticationConverter
.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
}
The configure
method can contain your existing configuration, where you might restrict or open up certain paths in the application. There is just one relevant line for teaching Spring about custom JWT claims, and that is.jwtAuthenticationConverter(jwtAuthenticationConverter())
. This line is wiring up the object created in the lower method to the configuration.
Let us walk through what is happening in the lower method. First, line 18 creates a JWTGrantedAuthoritiesConverter
instance. Line 20 uses .setAuthoritiesClaimName("roles")
to configure the name of the JWT claim that contains the user roles. Line 21 uses .setAuthorityPrefix("ROLE_")
to configure the prefix for the elements found in that JWT claim. Finally, line 22 to 26 create a JWTAuthenticationConverter
, use it to wrap the JWTGrantedAuthoritiesConverter
and return it.
If your application is configured like this, Spring Security takes the user roles from the roles
claim instead of the default scope
. It prefixes each user role that is present in the JWT token with ROLE_
. If you take a look at the Principal instance again that we injected into the controller method at the start of this post, you will see that its authorities
field now contains e.g. ROLE_admin
. This enables you to use@PreAuthorize("hasAuthority('ROLE_admin')"
on controller methods. Or .anyRequest().hasAuthority("ROLE_admin")
in the configure
method in the SecurityConfig class.
0 Comments