環境:複数のインフラストラクチャサービスとリソースサービス(ビジネスロジックを含む)で構成される、スプリングブートベースのマイクロサービスアーキテクチャアプリケーションがあります。承認と認証は、ユーザーエンティティを管理し、クライアントのJWTトークンを作成するoAuth2-Serviceによって処理されます。
単一のマイクロサービスアプリケーション全体をテストするために、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())
}
}
ここで、セキュリティ(リソースサーバー)の構成は次のとおりです。
@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))
_@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に設定します。
私は同じ問題を抱えていました、そして私が見つけた唯一の方法はトークンを作成してそれを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);
}
}