Nouveauté de Spring Security OAuth2 - Vérifier les revendications

Nouveautés dans Spring Security OAuth2 - Verify Claims

1. Vue d'ensemble

Dans ce rapide didacticiel, nous travaillerons avec une implémentation Spring Security OAuth2 et nous apprendrons comment vérifier les revendications JWT à l'aide du nouveauJwtClaimsSetVerifier - introduit dansSpring Security OAuth 2.2.0.RELEASE.

2. Configuration Maven

Tout d'abord, nous devons ajouter la dernière version despring-security-oauth2 dans nospom.xml:


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

3. Configuration du magasin de jetons

Ensuite, configurons nosTokenStore dans le serveur de ressources:

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

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

Notez comment nous ajoutons le nouveau vérificateur à nosJwtAccessTokenConverter.

Pour plus de détails sur la configuration deJwtTokenStore, consultez la description deusing JWT with Spring Security OAuth.

À présent, dans les sections suivantes, nous aborderons les différents types de vérificateur de revendications et la manière de les faire fonctionner ensemble.

4. IssuerClaimVerifier

Nous allons commencer simplement - en vérifiant la réclamation de l'émetteur «iss» à l'aide deIssuerClaimVerifier - comme suit:

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

Dans cet exemple, nous avons ajouté un simpleIssuerClaimVerifier pour vérifier notre émetteur. Si le jeton JWT contient une valeur différente pour la revendication de l'émetteur «iss», un simpleInvalidTokenException sera émis.

Naturellement, si le jeton contient la revendication «iss» de l'émetteur, aucune exception ne sera levée et le jeton est considéré comme valide.

5. Vérificateur de réclamations personnalisées

Mais ce qui est intéressant ici, c'est que nous pouvons également créer notre vérificateur de réclamation personnalisé:

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

Voici une implémentation simple de ce à quoi cela peut ressembler - pour vérifier si la revendicationuser_name existe dans notre jeton 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");
        }
    }
}

Remarquez comment nous implémentons simplement l'interfaceJwtClaimsSetVerifier ici, puis fournissons une implémentation entièrement personnalisée pour la méthode verify - ce qui nous donne une flexibilité totale pour tout type de contrôle dont nous avons besoin.

6. Combiner plusieurs vérificateurs de réclamation

Enfin, voyons comment combiner plusieurs vérificateurs de revendications à l'aide deDelegatingJwtClaimsSetVerifier - comme suit:

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

DelegatingJwtClaimsSetVerifier prend une liste d'objetsJwtClaimsSetVerifier et délègue le processus de vérification des revendications à ces vérificateurs.

7. Test d'intégration simple

Maintenant que nous en avons terminé avec la mise en œuvre, testons nos vérificateurs de revendications avec un simpleintegration test:

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

    @Autowired
    private JwtTokenStore tokenStore;

    ...
}

Nous allons commencer par un jeton qui ne contient pas d'émetteur (mais contient unuser_name) - qui doit être valide:

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

    assertTrue(auth.isAuthenticated());
}

La raison pour laquelle cela est valide est simple: le premier vérificateur n'est actif que s'il existe une revendication d'émetteur dans le jeton. Si cette revendication n’existe pas, le vérificateur n’intervient pas.

Examinons ensuite un jeton qui contient un émetteur valide (http://localhost:8081) et unuser_name également. Cela devrait également être valide:

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

    assertTrue(auth.isAuthenticated());
}

Lorsque le jeton contient un émetteur non valide (http://localhost:8082), il sera alors vérifié et jugé non valide:

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

    assertTrue(auth.isAuthenticated());
}

Ensuite, lorsque le jeton ne contient pas de réclamationuser_name, il est alors invalide:

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

    assertTrue(auth.isAuthenticated());
}

Et enfin, lorsque le jeton contient une revendicationuser_name vide, il est également invalide:

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

    assertTrue(auth.isAuthenticated());
}

8. Conclusion

Dans cet article rapide, nous avons examiné la nouvelle fonctionnalité de vérificateur dans Spring Security OAuth.

Comme toujours, le code source complet est disponibleover on GitHub.