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

时间:2019-03-16 02:57:46

标签: spring-boot testing oauth-2.0 mocking

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

到目前为止,一切都很好。

但是现在我正在编写测试,它们都未经授权返回错误401。我已经知道,这是因为测试未向OAuth2服务器发出任何请求。

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

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分: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生成功能提取到专用类中。为此添加一些构造函数参数(或使用构建器模式),并将其用于所有我的模拟返回。这样,您就可以测试正确,不正确以及意外的情况。

答案 2 :(得分: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());
    }

}