Usando JWT com Spring Security OAuth

Usando JWT com Spring Security OAuth

1. Visão geral

Neste tutorial, discutiremos como obter nossa implementação Spring Security OAuth2 para usar Tokens da Web JSON.

Também estamos continuando a construir sobrethe previous article nesta série OAuth.

Leitura adicional:

Logout em um aplicativo protegido OAuth

Um aprofundamento prático sobre como implementar logout em um aplicativo Spring Security OAuth2 com JWT.

Read more

OAuth2 Lembre-se de mim com o token de atualização

Aprenda a implementar a funcionalidade de lembrança de mim com um front end Angular, para um aplicativo protegido com Spring Security e OAuth2.

Read more

OAuth2 para uma API REST do Spring - manipule o token de atualização no AngularJS

Aprendemos como armazenar o token de atualização em um aplicativo cliente AngularJS, como atualizar um token de acesso expirado e como aproveitar o proxy Zuul.

Read more

 

Antes de começarmos - uma observação importante. Lembre-se de quethe Spring Security core team is in the process of implementing a new OAuth2 stack - com alguns aspectos já destacados e outros ainda em andamento.

Aqui está um vídeo rápido que lhe dará algum contexto sobre esse esforço:

 

Tudo bem, vamos começar.

2. Configuração do Maven

Primeiro, precisamos adicionar a dependênciaspring-security-jwt ao nossopom.xml:


    org.springframework.security
    spring-security-jwt

Observe que precisamos adicionar a dependênciaspring-security-jwt para o Authorization Server e o Resource Server.

3. Servidor de Autorização

A seguir, configuraremos nosso servidor de autorização para usarJwtTokenStore - da seguinte maneira:

@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;
    }
}

Observe que usamossymmetric key em nossoJwtAccessTokenConverter para assinar nossos tokens - o que significa que precisaremos usar a mesma chave exata para o Servidor de Recursos também.

4. Servidor de Recursos

Agora, vamos dar uma olhada em nossa configuração do servidor de recursos - que é muito semelhante à configuração do servidor de autorização:

@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;
    }
}

Lembre-se de que estamos definindo esses dois servidores como totalmente separados e implantáveis ​​de forma independente. Essa é a razão pela qual precisamos declarar alguns dos mesmos beans novamente aqui, na nova configuração.

5. Reivindicações personalizadas no token

Vamos agora configurar alguma infraestrutura para poder adicionar algunscustom claims in the Access Token. As declarações padrão fornecidas pela estrutura são muito boas, mas na maioria das vezes precisaremos de algumas informações extras no token para utilizar no lado do cliente.

Definiremos umTokenEnhancer para personalizar nosso token de acesso com essas declarações adicionais.

No exemplo a seguir, adicionaremos um campo extra “organization” ao nosso token de acesso - com esteCustomTokenEnhancer:

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;
    }
}

Em seguida, conectaremos isso em nossa configuraçãoAuthorization Server - da seguinte maneira:

@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();
}

Com essa nova configuração instalada e em execução, veja como seria uma carga útil de token:

{
    "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. Use o token de acesso no cliente JS

Por fim, queremos usar as informações do token em nosso aplicativo cliente AngualrJS. Usaremos a bibliotecaangular-jwt para isso.

Então, o que vamos fazer é usar a declaração “organization” em nossoindex.html: