java 在 oAuth2 资源服务器应用程序中使用 @WithMockUser(与 @SpringBootTest)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/41824885/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Use @WithMockUser (with @SpringBootTest) inside an oAuth2 Resource Server Application
提问by David
Environment: I have a spring boot based microservice architecture application consisting of multiple infrastructural services and resource services (containing the business logic). Authorization and authentication is handled by an oAuth2-Service managing the user entities and creating JWT tokens for the clients.
环境:我有一个基于 Spring Boot 的微服务架构应用程序,它由多个基础设施服务和资源服务(包含业务逻辑)组成。授权和身份验证由 oAuth2-Service 处理,该服务管理用户实体并为客户端创建 JWT 令牌。
To test a single microservice application in its entirety i tried to build tests with testNG, spring.boot.test, org.springframework.security.test...
为了完整地测试单个微服务应用程序,我尝试使用testNG、spring.boot.test、org.springframework.security.test构建测试......
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, properties = {"spring.cloud.discovery.enabled=false", "spring.cloud.config.enabled=false", "spring.profiles.active=test"})
@AutoConfigureMockMvc
@Test
public class ArtistControllerTest extends AbstractTestNGSpringContextTests {
@Autowired
private MockMvc mvc;
@BeforeClass
@Transactional
public void setUp() {
// nothing to do
}
@AfterClass
@Transactional
public void tearDown() {
// nothing to do here
}
@Test
@WithMockUser(authorities = {"READ", "WRITE"})
public void getAllTest() throws Exception {
// EXPECT HTTP STATUS 200
// BUT GET 401
this.mvc.perform(get("/")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
}
}
where the security (resource server) config is the following
其中安全(资源服务器)配置如下
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
// get the configured token store
@Autowired
TokenStore tokenStore;
// get the configured token converter
@Autowired
JwtAccessTokenConverter tokenConverter;
/**
* !!! configuration of springs http security !!!
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/**").authenticated();
}
/**
* configuration of springs resource server security
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// set the configured tokenStore to this resourceServer
resources.resourceId("artist").tokenStore(tokenStore);
}
}
and the following method based security check annotated inside the controller class
以及在控制器类中注释的以下基于方法的安全检查
@PreAuthorize("hasAuthority('READ')")
@RequestMapping(value = "/", method = RequestMethod.GET)
public List<Foo> getAll(Principal user) {
List<Foo> foos = fooRepository.findAll();
return foos;
}
I thought that would work but when running the test i only get an assertion error
我认为这会奏效,但在运行测试时,我只收到断言错误
java.lang.AssertionError: Status
Expected :200
Actual :401
Question:
Is there something totally obvious that i am doing wrong? Or is @WithMockUser not going to work with @SpringBootTest and @AutoConfigureMockMvc in an oAuth2 environment? If this is the case... what would be the best approach for testing route and method based security configurations as part of such an (integration) test like this one?
问题:我做错了什么很明显吗?或者@WithMockUser 不会在 oAuth2 环境中与 @SpringBootTest 和 @AutoConfigureMockMvc 一起工作?如果是这种情况......作为此类(集成)测试的一部分,测试基于路由和方法的安全配置的最佳方法是什么?
Appendix:
I also tried different approaches like something like the following... but it led to the same result :(
附录:我也尝试过类似以下的不同方法......但它导致了相同的结果:(
this.mvc.perform(get("/")
.with(user("admin").roles("READ","WRITE").authorities(() -> "READ", () -> "WRITE"))
.accept(MediaType.APPLICATION_JSON))
回答by Jochen Christ
@WithMockUser
creates the authentication in SecurityContext.
Same applies for with(user("username"))
.
@WithMockUser
在SecurityContext 中创建身份验证。同样适用于with(user("username"))
.
By default the OAuth2AuthenticationProcessingFilterdoes not use the SecurityContext, but always build the authentication from the token ("stateless").
默认情况下,OAuth2AuthenticationProcessingFilter不使用 SecurityContext,但始终从令牌(“无状态”)构建身份验证。
You can easily change this behavior be setting the stateless flag in the resource server security configuration to false:
您可以通过将资源服务器安全配置中的无状态标志设置为 false 来轻松更改此行为:
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration implements ResourceServerConfigurer {
@Override
public void configure(ResourceServerSecurityConfigurer security) throws Exception {
security.stateless(false);
}
@Override
public void configure(HttpSecurity http) {}
}
Another option is to extend ResourceServerConfigurerAdapter, but the problem with that is that it comes with configuration that forces all requests to be authenticated. Implementing the interface leaves your main security config unchanged apart from the statelessness.
另一种选择是扩展 ResourceServerConfigurerAdapter,但问题在于它带有强制所有请求都经过身份验证的配置。除了无状态之外,实现接口使您的主要安全配置保持不变。
Of course, set the flag to to falsein your test contexts, only.
当然,仅在您的测试上下文中将标志设置为false。
回答by Laureano Gabriel Clausi
I had de same issue, and the only way I found was creating a token and using it in the mockMvc perform
我遇到了同样的问题,我发现的唯一方法是创建一个令牌并在 mockMvc perform 中使用它
mockMvc.perform(get("/resource")
.with(oAuthHelper.bearerToken("test"))
And the OAuthHelper:
和 OAuthHelper:
@Component
@EnableAuthorizationServer
public class OAuthHelper extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthorizationServerTokenServices tokenservice;
@Autowired
ClientDetailsService clientDetailsService;
public RequestPostProcessor bearerToken(final String clientid) {
return mockRequest -> {
OAuth2AccessToken token = createAccessToken(clientid);
mockRequest.addHeader("Authorization", "Bearer " + token.getValue());
return mockRequest;
};
}
OAuth2AccessToken createAccessToken(final String clientId) {
ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
Collection<GrantedAuthority> authorities = client.getAuthorities();
Set<String> resourceIds = client.getResourceIds();
Set<String> scopes = client.getScope();
Map<String, String> requestParameters = Collections.emptyMap();
boolean approved = true;
String redirectUrl = null;
Set<String> responseTypes = Collections.emptySet();
Map<String, Serializable> extensionProperties = Collections.emptyMap();
OAuth2Request oAuth2Request = new OAuth2Request(requestParameters, clientId, authorities,
approved, scopes, resourceIds, redirectUrl, responseTypes, extensionProperties);
User userPrincipal = new User("user", "", true, true, true, true, authorities);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities);
OAuth2Authentication auth = new OAuth2Authentication(oAuth2Request, authenticationToken);
return tokenservice.createAccessToken(auth);
}
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test")
.authorities("READ");
}
}
回答by piepera
As I was specifically trying to write tests against our ResourceServerConfiguration, I worked around the issue by creating a test wrapper for it which set security.stateless to false:
当我专门尝试针对我们的 ResourceServerConfiguration 编写测试时,我通过为它创建一个测试包装器将 security.stateless 设置为 false 来解决这个问题:
@Configuration
@EnableResourceServer
public class ResourceServerTestConfiguration extends ResourceServerConfigurerAdapter {
private ResourceServerConfiguration configuration;
public ResourceServerTestConfiguration(ResourceServerConfiguration configuration) {
this.configuration = configuration;
}
@Override
public void configure(ResourceServerSecurityConfigurer security) throws Exception {
configuration.configure(security);
security.stateless(false);
}
@Override
public void configure(HttpSecurity http) throws Exception {
configuration.configure(http);
}
}