I've configured my SecurityFilterChain with .oauth2ResourceServer as follows
.oauth2ResourceServer(resourceServerConfigurer -> {
resourceServerConfigurer.jwt(jwtConfigurer -> {
final var jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(myJwtGrantedAuthoritiesMapper);
jwtConfigurer.jwtAuthenticationConverter(jwtAuthenticationConverter);
});
})
.authorizeHttpRequests(requests -> requests.anyRequest().authenticated());
This works fine and SecurityContextHolder.getAuthentication().getPrincipal()
gives me the Jwt
, so for example if I want the user's first name I can call jwt.getClaimAsString("given_name")
.
I think I'd like to avoid having to access the user's data this way, and I think it'd be nice to go a step further and map some data from the Jwt into my own MyUser class. It would be convenient for me if the authentication principal could be an instance of MyUser class.
What's the correct way to go about adding further logic to map the JwtAuthenticationToken /Jwt to another authentication token whose principal is an instance of MyUser?
jwtConfigurer.jwtAuthenticationConverter(jwtAuthenticationConverter)
is most frequently used to customize the claim(s) authorities are mapped from, but you can provide it with any Converter<Jwt, ? extends AbstractAuthenticationToken>
building any specialization of AbstractAuthenticationToken
(not necessarily a JwtAuthenticationToken
as done by JwtAuthenticationConverter
).
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
static interface AuthoritiesConverter extends Converter<Jwt, Collection<? extends GrantedAuthority>> {}
static interface AuthenticationConverter extends Converter<Jwt, OAuthentication<OpenidClaimSet>> {}
@SuppressWarnings("unchecked")
@Bean
AuthoritiesConverter authoritiesConverter() {
return jwt -> {
final Map<String, Object> realmAccess = (Map<String, Object>) jwt.getClaims().getOrDefault("realm_access", Map.of());
final List<String> realmRoles = (List<String>) realmAccess.getOrDefault("roles", List.of());
return realmRoles.stream().map(SimpleGrantedAuthority::new).toList();
};
}
@Bean
AuthenticationConverter authenticationConverter(Converter<Jwt, Collection<? extends GrantedAuthority>> authoritiesConverter) {
return jwt -> new OAuthentication<>(new OpenidClaimSet(jwt.getClaims()), authoritiesConverter.convert(jwt), jwt.getTokenValue());
}
@Bean
SecurityFilterChain filterChain(
HttpSecurity http,
Converter<Jwt, ? extends AbstractAuthenticationToken> authenticationConverter)
throws Exception {
http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(authenticationConverter)));
http.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).csrf(csrf -> csrf.disable());
http.exceptionHandling(eh -> eh.authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Bearer realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
}));
// @formatter:off
http.authorizeHttpRequests(requests -> requests
.requestMatchers(new AntPathRequestMatcher("/public/**")).permitAll()
.anyRequest().authenticated());
// @formatter:on
return http.build();
}
}
The OAuthentication
and OpenidClaimSet
above are defined in com.c4-soft.springaddons:spring-addons-oauth2:7.6.13
. The source are in this Github repo. You could find it useful as base for your own AbstractAuthenticationToken
implementation, as I do for instance in this tutorial were I build a custom security DSL based on private claims.