J'ai mon serveur OAUTH2, dont les autres services ont besoin pour demander un jeton jwt pour pouvoir accéder aux points de terminaison.
Jusqu'ici tout va bien, tout va bien.
Mais maintenant j'écris les tests, et ils renvoient tous l'erreur 401 sans autorisation. J'ai déjà compris que c'est parce que les tests ne font aucune requête pour le serveur OAuth2.
Ce que je voulais savoir, c'est comment faire une simulation à partir d'un JWT ou du serveur oauth2.
3 Réponses :
Le moyen le plus simple est de simuler directement un jeton JWT, vous pouvez utiliser une clé de signature différente pour vos tests afin de ne pas créer de coupure de sécurité pour votre backend.
Comment puis-je faire cela? Avez-vous un exemple?
vous pouvez créer votre propre jeton JWT en utilisant un framework, il y en a beaucoup sur jwt.io
Si vous avez besoin de simuler un JWT, la meilleure solution est d'utiliser un générateur JWT ciblant vos tests en utilisant Bibliothèque Nimbus JWT + JOSE
Par exemple, ci-dessous est un code directement extrait de JSON Web Token (JWT) avec signature RSA , qui montre la génération JWT ainsi que l'assertion qui est très très similaire à un test.
import java.util.Date; import com.nimbusds.jose.*; import com.nimbusds.jose.crypto.*; import com.nimbusds.jose.jwk.*; import com.nimbusds.jose.jwk.gen.*; import com.nimbusds.jwt.*; // RSA signatures require a public and private RSA key pair, the public key // must be made known to the JWS recipient in order to verify the signatures RSAKey rsaJWK = new RSAKeyGenerator(2048) .keyID("123") .generate(); RSAKey rsaPublicJWK = rsaJWK.toPublicJWK(); // Create RSA-signer with the private key JWSSigner signer = new RSASSASigner(rsaJWK); // Prepare JWT with claims set JWTClaimsSet claimsSet = new JWTClaimsSet.Builder() .subject("alice") .issuer("https://c2id.com") .expirationTime(new Date(new Date().getTime() + 60 * 1000)) .build(); SignedJWT signedJWT = new SignedJWT( new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(rsaJWK.getKeyID()).build(), claimsSet); // Compute the RSA signature signedJWT.sign(signer); // To serialize to compact form, produces something like // eyJhbGciOiJSUzI1NiJ9.SW4gUlNBIHdlIHRydXN0IQ.IRMQENi4nJyp4er2L // mZq3ivwoAjqa1uUkSBKFIX7ATndFF5ivnt-m8uApHO4kfIFOrW7w2Ezmlg3Qd // maXlS9DhN0nUk_hGI3amEjkKd0BWYCB8vfUbUv0XGjQip78AI4z1PrFRNidm7 // -jPDm5Iq0SZnjKjCNS5Q15fokXZc8u0A String s = signedJWT.serialize(); // On the consumer side, parse the JWS and verify its RSA signature signedJWT = SignedJWT.parse(s); JWSVerifier verifier = new RSASSAVerifier(rsaPublicJWK); assertTrue(signedJWT.verify(verifier)); // Retrieve / verify the JWT claims according to the app requirements assertEquals("alice", signedJWT.getJWTClaimsSet().getSubject()); assertEquals("https://c2id.com", signedJWT.getJWTClaimsSet().getIssuer()); assertTrue(new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime()));
Ce que je ferais, c'est d'extraire des fonctionnalités de génération JWT similaires dans une classe dédiée. Ajoutez quelques paramètres de constructeur (ou utilisez un modèle de constructeur) à cela et utilisez-le pour tous mes retours simulés. De cette façon, vous pouvez tester les scénarios corrects, incorrects et inattendus.
J'ai essayé les deux astuces mais sans succès.
Ma configuration de serveur de ressources:
@Configuration public class ConfiguracaoDeToken { @Autowired private ConversorDeTokenDeAcessoPersonalizado conversorDeTokenDeAcessoPersonalizado; @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Bean public DefaultTokenServices tokenServices() { final DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); defaultTokenServices.setTokenStore(tokenStore()); return defaultTokenServices; } @Bean public JwtAccessTokenConverter accessTokenConverter() { final JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setAccessTokenConverter(conversorDeTokenDeAcessoPersonalizado); final Resource resource = new ClassPathResource("public.txt"); String publicKey = null; try { publicKey = IOUtils.toString(resource.getInputStream()); } catch (final IOException e) { throw new RuntimeException(e); } converter.setVerifierKey(publicKey); return converter; } } @Component public class ConversorDeTokenDeAcessoPersonalizado extends DefaultAccessTokenConverter { @Override public OAuth2Authentication extractAuthentication(Map<String, ?> claims) { OAuth2Authentication authentication = super.extractAuthentication(claims); authentication.setDetails(claims); return authentication; } } @Component public class TokenPayload { private Map<String, Object> getExtraInfo() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); OAuth2AuthenticationDetails oauthDetails = (OAuth2AuthenticationDetails) auth.getDetails(); @SuppressWarnings("unchecked") var details = (Map<String, Object>) oauthDetails.getDecodedDetails(); return details; } public String payloadLogin() { return getExtraInfo().get("user_name").toString(); } public Long payloadIdEmpresa() { return Long.parseLong(getExtraInfo().get("idEmpresa").toString()); } public Long payloadIdFuncionario() { return Long.parseLong(getExtraInfo().get("idFuncionario").toString()); } }
Ma configuration de jeton
@Configuration @EnableResourceServer public class ServidorDeRecursos extends ResourceServerConfigurerAdapter { @Autowired private ConfiguracaoDeToken configuracaoDeToken; @Override public void configure(final HttpSecurity http) throws Exception { // @formatter:off http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .authorizeRequests().anyRequest().permitAll(); // @formatter:on } @Override public void configure(final ResourceServerSecurityConfigurer config) { config.tokenServices(configuracaoDeToken.tokenServices()); } }