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.

Categories: Spring Framework

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *