Verwendung von JWT mit Spring Security OAuth

Verwenden von JWT mit Spring Security OAuth

1. Überblick

In diesem Tutorial wird erläutert, wie Sie unsere Spring Security OAuth2-Implementierung dazu bringen, JSON-Web-Token zu verwenden.

In dieser OAuth-Serie bauen wir auch weiterhin aufthe previous article auf.

Weitere Lektüre:

Melden Sie sich in einer OAuth Secured-Anwendung ab

Praktische Einführung in die Implementierung der Abmeldung in einer Spring Security OAuth2-Anwendung mit JWT.

Read more

OAuth2 Erinnere dich an mich mit Refresh Token

Erfahren Sie, wie Sie die Remember-Me-Funktionalität mit einem Angular-Frontend für eine mit Spring Security und OAuth2 gesicherte Anwendung implementieren.

Read more

OAuth2 für eine Spring-REST-API - Behandeln Sie das Aktualisierungstoken in AngularJS

Wir haben gelernt, wie Sie das Aktualisierungstoken in einer AngularJS-Client-App speichern, wie Sie ein abgelaufenes Zugriffstoken aktualisieren und wie Sie den Zuul-Proxy nutzen.

Read more

 

Bevor wir anfangen - ein wichtiger Hinweis. Denken Sie daran, dassthe Spring Security core team is in the process of implementing a new OAuth2 stack - mit einigen Aspekten bereits und einige noch in Bearbeitung.

Hier ist ein kurzes Video, das Ihnen einen Kontext zu diesen Bemühungen gibt:

 

Okay, lass uns gleich einsteigen.

2. Maven-Konfiguration

Zuerst müssen wir die Abhängigkeit vonspring-security-jwtzu unserenpom.xmlhinzufügen:


    org.springframework.security
    spring-security-jwt

Beachten Sie, dass wir die Abhängigkeit vonspring-security-jwtowohl zum Autorisierungsserver als auch zum Ressourcenserver hinzufügen müssen.

3. Autorisierungsserver

Als Nächstes konfigurieren wir unseren Autorisierungsserver für die Verwendung vonJwtTokenStore - wie folgt:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
      throws Exception {
        endpoints.tokenStore(tokenStore())
                 .accessTokenConverter(accessTokenConverter())
                 .authenticationManager(authenticationManager);
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
}

Beachten Sie, dass wir in unserenJwtAccessTokenConverter einsymmetric key verwendet haben, um unsere Token zu signieren. Dies bedeutet, dass wir denselben genauen Schlüssel auch für den Resources Server verwenden müssen.

4. Ressourcenserver

Schauen wir uns nun unsere Resource Server-Konfiguration an, die der Konfiguration des Authorization Servers sehr ähnlich ist:

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(ResourceServerSecurityConfigurer config) {
        config.tokenServices(tokenServices());
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }
}

Beachten Sie, dass wir diese beiden Server als vollständig getrennt und unabhängig voneinander bereitstellbar definieren. Aus diesem Grund müssen wir hier in der neuen Konfiguration einige der gleichen Beans erneut deklarieren.

5. Benutzerdefinierte Ansprüche im Token

Richten wir nun eine Infrastruktur ein, um einigecustom claims in the Access Tokenhinzufügen zu können. Die vom Framework bereitgestellten Standardansprüche sind alle gut und schön, aber meistens benötigen wir einige zusätzliche Informationen im Token, um sie auf der Clientseite verwenden zu können.

Wir definierenTokenEnhancer, um unser Zugriffstoken mit diesen zusätzlichen Ansprüchen anzupassen.

Im folgenden Beispiel fügen wir unserem Zugriffstoken ein zusätzliches Feld "organization" hinzu - mit diesemCustomTokenEnhancer:

public class CustomTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(
      OAuth2AccessToken accessToken,
      OAuth2Authentication authentication) {
        Map additionalInfo = new HashMap<>();
        additionalInfo.put(
          "organization", authentication.getName() + randomAlphabetic(4));
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(
          additionalInfo);
        return accessToken;
    }
}

Dann werden wir das wie folgt in die Konfiguration vonAuthorization Servereinbinden:

@Override
public void configure(
  AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
    tokenEnhancerChain.setTokenEnhancers(
      Arrays.asList(tokenEnhancer(), accessTokenConverter()));

    endpoints.tokenStore(tokenStore())
             .tokenEnhancer(tokenEnhancerChain)
             .authenticationManager(authenticationManager);
}

@Bean
public TokenEnhancer tokenEnhancer() {
    return new CustomTokenEnhancer();
}

Mit dieser neuen Konfiguration würde eine Token-Token-Nutzlast folgendermaßen aussehen:

{
    "user_name": "john",
    "scope": [
        "foo",
        "read",
        "write"
    ],
    "organization": "johnIiCh",
    "exp": 1458126622,
    "authorities": [
        "ROLE_USER"
    ],
    "jti": "e0ad1ef3-a8a5-4eef-998d-00b26bc2c53f",
    "client_id": "fooClientIdPassword"
}

5.1. Verwenden Sie das Zugriffstoken im JS-Client

Schließlich möchten wir die Token-Informationen in unserer AngualrJS-Clientanwendung verwenden. Dafür verwenden wir die Bibliothekangular-jwt.

Wir werden also die Behauptung "organization" in unserenindex.html verwenden: