web-dev-qa-db-ja.com

oAuth2リソースサーバーアプリケーション内で@WithMockUser(@SpringBootTestを使用)を使用する

環境:複数のインフラストラクチャサービスとリソースサービス(ビジネスロジックを含む)で構成される、スプリングブートベースのマイクロサービスアーキテクチャアプリケーションがあります。承認と認証は、ユーザーエンティティを管理し、クライアントのJWTトークンを作成するoAuth2-Serviceによって処理されます。

単一のマイクロサービスアプリケーション全体をテストするために、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())
  }
}

ここで、セキュリティ(リソースサーバー)の構成は次のとおりです。

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

}

コントローラクラス内で注釈が付けられた次のメソッドベースのセキュリティチェック

@PreAuthorize("hasAuthority('READ')")
@RequestMapping(value = "/", method = RequestMethod.GET)
public List<Foo> getAll(Principal user) {
    List<Foo> foos = fooRepository.findAll();
    return foos;
}

私はそれはうまくいくと思っていましたが、テストを実行するとアサーションエラーしか表示されません

Java.lang.AssertionError: Status 
Expected :200
Actual   :401


質問:私が間違っていることは完全に明らかですか?または、@ AuthMockUserは、oAuth2環境で@SpringBootTestおよび@AutoConfigureMockMvcと連携しないのですか?この場合、このような(統合)テストの一部として、ルートおよびメソッドベースのセキュリティ構成をテストするための最良のアプローチは何でしょうか。


付録:次のような別のアプローチも試しましたが、同じ結果が得られました:(

this.mvc.perform(get("/")
        .with(user("admin").roles("READ","WRITE").authorities(() -> "READ", () -> "WRITE"))
        .accept(MediaType.APPLICATION_JSON))

参照
春のセキュリティテスト
スプリングブート1.4テスト

14
David

_@WithMockUser_はSecurityContextで認証を作成します。同じことがwith(user("username"))にも当てはまります。

デフォルトではOAuth2AuthenticationProcessingFilterはSecurityContextを使用しませんが、常にトークンから認証を構築します(「ステートレス」)。

リソースサーバーのセキュリティ構成でステートレスフラグを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) {}

}
_

別のオプションはResourceServerConfigurerAdapterを拡張することですが、それに関する問題は、すべての要求を認証するように強制する構成が付属していることです。インターフェースを実装すると、メインのセキュリティ構成がステートレスであること以外は変更されません。

もちろん、テストコンテキストでのみ、フラグをfalseに設定します。

18
Jochen Christ

私は同じ問題を抱えていました、そして私が見つけた唯一の方法はトークンを作成してそれをmockMvcで使用することでした

mockMvc.perform(get("/resource")
                    .with(oAuthHelper.bearerToken("test"))

そして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");
    }

}

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);
  }
}
0
piepera