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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 06:11:23  来源:igfitidea点击:

Use @WithMockUser (with @SpringBootTest) inside an oAuth2 Resource Server Application

javaspring-mvcspring-bootspring-securityspring-test-mvc

提问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...

为了完整地测试单个微服务应用程序,我尝试使用testNGspring.boot.testorg.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))

see:
spring security testing
spring boot 1.4 testing

参见
spring security testing
spring boot 1.4 testing

回答by Jochen Christ

@WithMockUsercreates the authentication in SecurityContext. Same applies for with(user("username")).

@WithMockUserSecurityContext 中创建身份验证。同样适用于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);
  }
}