Novo no Spring Security OAuth2 - Verificar reivindicações

Novo no Spring Security OAuth2 - Verificar reivindicações

1. Visão geral

Neste tutorial rápido, trabalharemos com uma implementação Spring Security OAuth2 e aprenderemos como verificar as declarações JWT usando o novoJwtClaimsSetVerifier - introduzido emSpring Security OAuth 2.2.0.RELEASE.

2. Configuração do Maven

Primeiro, precisamos adicionar a versão mais recente despring-security-oauth2 em nossopom.xml:


    org.springframework.security.oauth
    spring-security-oauth2
    2.2.0.RELEASE

3. Configuração de Token Store

A seguir, vamos configurar nossoTokenStore no servidor de recursos:

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

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

Observe como estamos adicionando o novo verificador ao nossoJwtAccessTokenConverter.

Para obter mais detalhes sobre como configurarJwtTokenStore, verifique o artigo sobreusing JWT with Spring Security OAuth.

Agora, nas seções a seguir, discutiremos diferentes tipos de verificador de declaração e como fazê-los trabalhar juntos.

4. IssuerClaimVerifier

Começaremos de maneira simples - verificando a declaração do emissor “iss” usandoIssuerClaimVerifier - da seguinte forma:

@Bean
public JwtClaimsSetVerifier issuerClaimVerifier() {
    try {
        return new IssuerClaimVerifier(new URL("http://localhost:8081"));
    } catch (MalformedURLException e) {
        throw new RuntimeException(e);
    }
}

Neste exemplo, adicionamos umIssuerClaimVerifier simples para verificar nosso emissor. Se o token JWT contiver um valor diferente para a declaração do emissor “iss”, umInvalidTokenException simples será lançado.

Naturalmente, se o token contiver a reivindicação "iss" do emissor, nenhuma exceção será lançada e o token será considerado válido.

5. Verificador de reivindicação personalizado

Mas, o que é interessante aqui é que também podemos construir nosso verificador de declaração personalizado:

@Bean
public JwtClaimsSetVerifier customJwtClaimVerifier() {
    return new CustomClaimVerifier();
}

Aqui está uma implementação simples de como isso pode ser - para verificar se a declaraçãouser_name existe em nosso token JWT:

public class CustomClaimVerifier implements JwtClaimsSetVerifier {
    @Override
    public void verify(Map claims) throws InvalidTokenException {
        String username = (String) claims.get("user_name");
        if ((username == null) || (username.length() == 0)) {
            throw new InvalidTokenException("user_name claim is empty");
        }
    }
}

Observe como estamos simplesmente implementando a interfaceJwtClaimsSetVerifier aqui e, em seguida, fornecemos uma implementação totalmente personalizada para o método de verificação - o que nos dá total flexibilidade para qualquer tipo de verificação de que precisamos.

6. Combine Multiple Claim Verifiers

Finalmente, vamos ver como combinar o verificador de múltiplas declarações usandoDelegatingJwtClaimsSetVerifier - da seguinte maneira:

@Bean
public JwtClaimsSetVerifier jwtClaimsSetVerifier() {
    return new DelegatingJwtClaimsSetVerifier(Arrays.asList(
      issuerClaimVerifier(), customJwtClaimVerifier()));
}

DelegatingJwtClaimsSetVerifier pega uma lista de objetosJwtClaimsSetVerifier e delega o processo de verificação de sinistro a esses verificadores.

7. Teste de integração simples

Agora que concluímos a implementação, vamos testar nossos verificadores de declarações com um simplesintegration test:

@RunWith(SpringRunner.class)
@SpringBootTest(
  classes = ResourceServerApplication.class,
  webEnvironment = WebEnvironment.RANDOM_PORT)
public class JwtClaimsVerifierIntegrationTest {

    @Autowired
    private JwtTokenStore tokenStore;

    ...
}

Começaremos com um token que não contém um emissor (mas contém umuser_name) - que deve ser válido:

@Test
public void whenTokenDontContainIssuer_thenSuccess() {
    String tokenValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....";
    OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);

    assertTrue(auth.isAuthenticated());
}

A razão pela qual isso é válido é simples - o primeiro verificador estará ativo apenas se houver uma solicitação de emissor no token. Se essa reivindicação não existir - o verificador não entra em ação.

A seguir, vamos dar uma olhada em um token que contém um emissor válido (http://localhost:8081) e umuser_name também. Isso também deve ser válido:

@Test
public void whenTokenContainValidIssuer_thenSuccess() {
    String tokenValue = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....";
    OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);

    assertTrue(auth.isAuthenticated());
}

Quando o token contém um emissor inválido (http://localhost:8082) - então, ele será verificado e considerado inválido:

@Test(expected = InvalidTokenException.class)
public void whenTokenContainInvalidIssuer_thenException() {
    String tokenValue = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....";
    OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);

    assertTrue(auth.isAuthenticated());
}

A seguir, quando o token não contém uma declaraçãouser_name, ele será inválido:

@Test(expected = InvalidTokenException.class)
public void whenTokenDontContainUsername_thenException() {
    String tokenValue = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....";
    OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);

    assertTrue(auth.isAuthenticated());
}

E, finalmente, quando o token contém uma declaraçãouser_name vazia, então também é inválido:

@Test(expected = InvalidTokenException.class)
public void whenTokenContainEmptyUsername_thenException() {
    String tokenValue = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....";
    OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);

    assertTrue(auth.isAuthenticated());
}

8. Conclusão

Neste artigo rápido, vimos a nova funcionalidade do verificador no Spring Security OAuth.

Como sempre, o código-fonte completo está disponívelover on GitHub.