我当前正在使用RemoteTokenServices
类:
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Value("${auth-server.url}")
private String authEndpoint;
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Value("${security.oauth2.client.client-secret}")
private String clientSecret;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("ms/legacy");
}
@Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId(clientId);
tokenServices.setClientSecret(clientSecret);
tokenServices.setCheckTokenEndpointUrl(authEndpoint + "/uaa/oauth/check_token");
return tokenServices;
}
}
我希望能够对所有端点集成测试轻松且正确地进行模拟,知道:
OncePerRequestFilter
中进行解码以获取一些关键信息有没有一种标准的方法来
预期的结果是,我可以只用几行额外的内容编写一个端点测试,以在请求中设置正确的JWT,并且令牌服务会愚蠢地同意其有效性。
答案 0 :(得分:0)
鉴于我们根本不想测试安全性,这种情况的最佳解决方案是:
@WithMockUser
和MockMvc
ResourceServerConfigurerAdapter
适应测试:@ActiveProfiles("!test")
)创建继承类security.stateless(false);
)test
配置文件这是在实践中的实现方式:
以ResourceServerConfigurerAdapter
为基础,因此配置在测试和非测试上下文之间具有主要的共同部分:
public class BaseResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("ms/legacy");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll().and().cors().disable().csrf().disable().httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.accessDeniedHandler(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED));
}
}
它的非测试实现:
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Profile("!test")
public class ResourceServerConfiguration extends BaseResourceServerConfiguration {
@Value("${auth-server.url}")
private String authEndpoint;
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Value("${security.oauth2.client.client-secret}")
private String clientSecret;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("ms/legacy");
}
@Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId(clientId);
tokenServices.setClientSecret(clientSecret);
tokenServices.setCheckTokenEndpointUrl(authEndpoint + "/uaa/oauth/check_token");
return tokenServices;
}
}
对于测试:
@Configuration
@EnableResourceServer
@ActiveProfiles("test")
public class TestResourceServerConfigurerAdapter extends BaseResourceServerConfiguration {
@Override
public void configure(ResourceServerSecurityConfigurer security) throws Exception {
super.configure(security);
// Using OAuth with distant authorization service, stateless implies that the request tokens
// are verified each time against this service. In test, we don't want that because we need
// properly isolated tests. Setting this implies that the security is checked only locally
// and allows us to mock it with @WithMockUser, @AutoConfigureMockMvc and autowired MockMVC
security.stateless(false);
}
}
使用测试请求过滤器插入令牌专用信息:
@Component
@ActiveProfiles("test")
public class TestRequestFilter extends OncePerRequestFilter {
private Optional<InfoConf> nextInfoConf = Optional.empty();
// Request info is our request-scoped bean that holds JWT info
@Autowired
private RequestInfo info;
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
if (nextInfoConf.isPresent()) {
info.setInfoConf(nextInfoConf.get());
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
public void setNextInfoConf(InfoConf nextInfoConf) {
this.nextInfoConf = Optional.of(nextInfoConf);
}
public void clearNextInfoConf() {
nextInfoConf = Optional.empty();
}
}
当然,当没有JWT时,让JWT解析什么也不做。
我们还编写了一个小的实用程序组件来创建要注入的相关信息。
典型的集成测试如下:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class TestClass {
@Autowired
protected MockMvc mockMvc;
@Before
public void before() {
// Create an user in DB
// Inject the related information in our filter
}
@After
public void after() {
// Cleanup both in DB and filter
}
@Test
@WithMockUser
public void testThing() throws Exception {
// Use MockMVC
}
}
另一种解决方案是确实模拟ResourceServerTokenServices
,但实际上要构建适当的令牌要痛苦得多,并且使用Spring的标准安全性模拟似乎更合适。