Wie konfiguriere ich Resource Server in Spring Security für die Verwendung zusätzlicher Informationen im JWT-Token?
Ich habe einen oauth2-JWT-Tokenserver konfiguriert, um zusätzliche Informationen zu den Benutzerberechtigungen festzulegen.
@Configuration
@Component
public class CustomTokenEnhancer extends JwtAccessTokenConverter {
CustomTokenEnhancer(){
super();
}
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
// TODO Auto-generated method stub
MyUserDetails user = (MyUserDetails) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
@SuppressWarnings("unchecked")
List<GrantedAuthority> authorities= (List<GrantedAuthority>) user.getAuthorities();
additionalInfo.put("authorities", authorities);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
Ich bin nicht sicher, wie ich meinen Ressourcenserver so konfigurieren soll, dass die vom oauth2-Server festgelegten Benutzerberechtigungen extrahiert werden und diese Berechtigung für mit @Secured annotierte Controller in Spring Security Framework verwendet wird.
Meine Auth-Serverkonfiguration sieht folgendermaßen aus:
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Value("${config.oauth2.privateKey}")
private String privateKey;
@Value("${config.oauth2.publicKey}")
private String publicKey;
@Value("{config.clienturl}")
private String clientUrl;
@Autowired
AuthenticationManager authenticationManager;
@Bean
public JwtAccessTokenConverter customTokenEnhancer(){
JwtAccessTokenConverter customTokenEnhancer = new CustomTokenEnhancer();
customTokenEnhancer.setSigningKey(privateKey);
return customTokenEnhancer;
}
@Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(customTokenEnhancer());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("isAnonymous() || hasRole('ROLE_TRUSTED_CLIENT')") // permitAll()
.checkTokenAccess("hasRole('TRUSTED_CLIENT')"); // isAuthenticated()
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.accessTokenConverter(customTokenEnhancer())
;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String url = clientUrl;
clients.inMemory()
.withClient("public")
.authorizedGrantTypes("client_credentials", "implicit")
.scopes("read")
.redirectUris(url)
.and()
.withClient("eagree_web").secret("eagree_web_dev")
//eagree_web should come from properties file?
.authorities("ROLE_TRUSTED_CLIENT")
.authorizedGrantTypes("client_credentials", "password", "authorization_code", "refresh_token")
.scopes("read", "write", "trust")
.redirectUris(url).resourceIds("dummy");
}
}
Und meine Resource Server-Konfiguration sieht folgendermaßen aus:
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Value("{config.oauth2.publicKey}")
private String publicKey;
@Autowired
CustomTokenEnhancer tokenConverter;
@Autowired
JwtTokenStore jwtTokenStore;
@Bean
public JwtTokenStore jwtTokenStore() {
tokenConverter.setVerifierKey(publicKey);
jwtTokenStore.setTokenEnhancer(tokenConverter);
return jwtTokenStore;
}
@Bean
public ResourceServerTokenServices defaultTokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenEnhancer(tokenConverter);
defaultTokenServices.setTokenStore(jwtTokenStore());
return defaultTokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
// @formatter:off
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.requestMatchers()
.antMatchers("/**")
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()
.antMatchers(HttpMethod.GET, "/api/**").access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.PATCH, "/api/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.POST, "/api/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PUT, "/api/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.DELETE, "/api/**").access("#oauth2.hasScope('write')")
.antMatchers("/admin/**").access("hasRole('ROLE_USER')");
// @formatter:on
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
System.out.println("Configuring ResourceServerSecurityConfigurer ");
resources.resourceId("dummy").tokenServices(defaultTokenServices());
}
}
Mein Testfall scheitert kläglich und sagt:
{"error": "invalid_token", "error_description": "Zugriffstoken kann nicht in JSON konvertiert werden"}
Wie erhalte ich das Authentifizierungsobjekt aus dem JWT? Wie authentifiziere ich den Client mit Clientanmeldeinformationen? Wie verwende ich @Secured Annotation auf meinen Ressourcencontrollern?
Welcher Code wird auf der Seite des Ressourcenservers verwendet, um das Token zu dekodieren, um Clientanmeldeinformationen zu extrahieren, und welcher Code wird für die Benutzerrolle überprüft?
Bitte hilf mir, da ich bereits 2 Tage damit verbracht habe, meinen Kopf auf diese scheinbar einfache Aufgabe zu stoßen.
Hinweis: Ich erhalte das Token vom Authentifizierungsserver als: {access_token = b5d89a13-3c8b-4bda-b0f2-a6e9d7b7a285, token_type = bearer, refresh_token = 43777224-b6f2-44d7-bf36-4e1934d32cb , authority = [{authority = ROLE_USER}, {authority = ROLE_ADMIN}]}
Bitte erläutern Sie die Konzepte und weisen Sie darauf hin, wenn in meiner Konfiguration etwas fehlt. Ich muss die Best Practices für die Konfiguration meiner Ressource und meines Authentifizierungsservers kennen.