我如何使用mock测试OAuth2资源服务器

问题描述 投票:1回答:3

我有我的OAUTH2服务器,其他服务需要请求一个jwt令牌才能访问端点。

到目前为止一切顺利,一切进展顺利。

但现在我正在编写测试,他们都会在未经授权的情况下返回错误401。我已经明白这是因为测试不会对OAuth2服务器发出任何请求。

我想知道的是如何从JWT或oauth2服务器进行模拟。

spring-boot testing oauth-2.0 mocking
3个回答
2
投票

最简单的方法是直接模拟JWT令牌,您可以为测试使用不同的签名密钥,这样您就不会为后端创建安全中断。


1
投票

如果你需要模拟JWT,最好的解决方案是使用Nimbus JWT + JOSE library定位你的测试的JWT生成器

例如,下面是直接从JSON Web Token (JWT) with RSA signature中提取的代码,它显示了JWT生成以及与测试非常相似的断言。

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

我要做的是将类似的JWT生成功能提取到专用类。添加几个构造函数参数(或使用构建器模式)并将其用于我的所有模拟返回。这样您就可以测试正确,不正确以及意外情况。


0
投票

我尝试了两个提示但没有成功。

我的资源服务器配置:

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

我的令牌配置

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

}
© www.soinside.com 2019 - 2024. All rights reserved.