私のコントローラクラスは、以下であります:
@PostMapping(path="/users/{id}")
@PreAuthorize("hasAnyAuthority('CAN_READ')")
public @ResponseBody ResponseEntity<User> getUser(@PathVariable int id) {
...
}
私は、次のリソースサーバーの設定を持っています
@Configuration
public class ResourceServerCofig implements ResourceServerConfigurer {
private static final String RESOURCE_ID = "test";
@Override
public void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
}
最後に、私のテストでは、次のようになります。
@RunWith(SpringRunner.class)
@WebMvcTest(ClientController.class)
public class ClientControllerTest {
@Autowired
private MockMvc mockMvc;
@WithMockUser(authorities={"CAN_READ"})
@Test
public void should_get_user_by_id() throws Exception {
...
mockMvc.perform(MockMvcRequestBuilders.get("/user/1")).
andExpect(MockMvcResultMatchers.status().isOk()).
andExpect(MockMvcResultMatchers.header().string(HttpHeaders.CONTENT_TYPE, "application/json")).
andExpect(MockMvcResultMatchers.jsonPath("$.name").value("johnson"));
}
}
問題は、私は常にメッセージと401 HTTPステータスを取得することですunauthorized","error_description":"Full authentication is required to access this resource
。
どのように私は@PreAuthorized注釈付きのコントローラのメソッドメソッドのテストを書くべきことができますか?
私はこの問題を解決する方法を考え出すの日の部分を費やしてきました。私はそんなに悪くないと思う、と多くを助けることができるという解決策になってしまいました。
あなたがテストで実行しようとしました何に基づいて、あなたが行うことができmockMvc
、あなたのコントローラをテストします。AuthorizationServerが呼び出されないことに注意してください。あなたは、テストのためのあなたのリソースサーバーでのみご利用いただけます。
- 豆の作成
InMemoryTokenStore
に使用されるOAuth2AuthenticationProcessingFilter
ユーザーを認証するために。どのような素晴らしいのは、あなたがあなたにトークンを追加できるということですInMemoryTokenStore
、あなたのテストを実行する前に。OAuth2AuthenticationProcessingFilter
彼が使用しているトークンに基づいてユーザーを認証し、任意のリモートサーバーを呼び出すことはありません。
@Configuration
public class AuthenticationManagerProvider {
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
- 注釈は
@WithMockUser
のOAuth2では動作しません。確かに、OAuth2AuthenticationProcessingFilter
常にあなたのトークン、のWi何点をチェックしませんSecurityContext
。私は同じアプローチを使用することをお勧めし@WithMockUser
ていますが、あなたのコードベースに作成したアノテーションを使用します。(一部の保守が容易でクリーンなテストを持っているために):
@WithMockOAuth2Scope
あなたの認証をカスタマイズする必要があり、ほぼすべてのパラメータが含まれています。あなたが使用することは決してありませんそれらを削除することができますが、私はあなたが可能性を見作るために多くを置きます。(テスト用フォルダに、これらの2つのクラスを入れて)
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockOAuth2ScopeSecurityContextFactory.class)
public @interface WithMockOAuth2Scope {
String token() default "";
String clientId() default "client-id";
boolean approved() default true;
String redirectUrl() default "";
String[] responseTypes() default {};
String[] scopes() default {};
String[] resourceIds() default {};
String[] authorities() default {};
String username() default "username";
String password() default "";
String email() default "";
}
その後、我々はこの注釈を解釈して、あなたのテストのために必要なデータとの `InMemoryTokenStoreを埋めるためにクラスが必要です。
@Component
public class WithMockOAuth2ScopeSecurityContextFactory implements WithSecurityContextFactory<WithMockOAuth2Scope> {
@Autowired
private TokenStore tokenStore;
@Override
public SecurityContext createSecurityContext(WithMockOAuth2Scope mockOAuth2Scope) {
OAuth2AccessToken oAuth2AccessToken = createAccessToken(mockOAuth2Scope.token());
OAuth2Authentication oAuth2Authentication = createAuthentication(mockOAuth2Scope);
tokenStore.storeAccessToken(oAuth2AccessToken, oAuth2Authentication);
return SecurityContextHolder.createEmptyContext();
}
private OAuth2AccessToken createAccessToken(String token) {
return new DefaultOAuth2AccessToken(token);
}
private OAuth2Authentication createAuthentication(WithMockOAuth2Scope mockOAuth2Scope) {
OAuth2Request oauth2Request = getOauth2Request(mockOAuth2Scope);
return new OAuth2Authentication(oauth2Request,
getAuthentication(mockOAuth2Scope));
}
private OAuth2Request getOauth2Request(WithMockOAuth2Scope mockOAuth2Scope) {
String clientId = mockOAuth2Scope.clientId();
boolean approved = mockOAuth2Scope.approved();
String redirectUrl = mockOAuth2Scope.redirectUrl();
Set<String> responseTypes = new HashSet<>(asList(mockOAuth2Scope.responseTypes()));
Set<String> scopes = new HashSet<>(asList(mockOAuth2Scope.scopes()));
Set<String> resourceIds = new HashSet<>(asList(mockOAuth2Scope.resourceIds()));
Map<String, String> requestParameters = Collections.emptyMap();
Map<String, Serializable> extensionProperties = Collections.emptyMap();
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(mockOAuth2Scope.authorities());
return new OAuth2Request(requestParameters, clientId, authorities,
approved, scopes, resourceIds, redirectUrl, responseTypes, extensionProperties);
}
private Authentication getAuthentication(WithMockOAuth2Scope mockOAuth2Scope) {
List<GrantedAuthority> grantedAuthorities = AuthorityUtils.createAuthorityList(mockOAuth2Scope.authorities());
String username = mockOAuth2Scope.username();
User userPrincipal = new User(username,
mockOAuth2Scope.password(),
true, true, true, true, grantedAuthorities);
HashMap<String, String> details = new HashMap<>();
details.put("user_name", username);
details.put("email", mockOAuth2Scope.email());
TestingAuthenticationToken token = new TestingAuthenticationToken(userPrincipal, null, grantedAuthorities);
token.setAuthenticated(true);
token.setDetails(details);
return token;
}
}
- すべてが設定されたら、下の簡単なテストクラスを作成します
src/test/java/your/package/
。このクラスは行いますmockMvc
操作をし、使用し@ WithMockOAuth2Scope
、あなたのテストのために必要なトークンを作成します。
@WebMvcTest(SimpleController.class)
@Import(AuthenticationManagerProvider.class)
class SimpleControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockOAuth2Scope(token = "123456789",
authorities = "CAN_READ")
public void test() throws Exception {
mockMvc.perform(get("/whoami")
.header("Authorization", "Bearer 123456789"))
.andExpect(status().isOk())
.andExpect(content().string("username"));
}
}
私は、このソリューションのおかげに思い付きました:
- どのように春 - セキュリティのOAuth2リソースサーバのセキュリティをテストするには?
- 偽造のOAuth2シングルサインオンの春、二つの方法で
- 注釈ソリューション
- 流暢APIソリューション
- 私は春の内側に自分自身を失ったが、私は最終的に私が探していたものを見つけ、デバッグ、多くの。
好奇心のために:
春のロード、テストする場合InMemoryTokenStore
、およびあなたが(として提供する1つを取る可能性を与えます@Bean
)。生産で実行している場合は、私の場合のために、春が使用するRemoteTokenStore
トークンをチェックするために、リモート認証サーバを呼び出します、( http://authorization_server/oauth/check_token
)。
あなたがのOAuth2を使用することを決定したとき、春は発射しますOAuth2AuthenticationProcessingFilter
。これは、すべて私のデバッグセッション中に、私のエントリポイントでした。
私はこのためにあなたに感謝し、多くのことを学びました。
あなたは見つけることができ、ここでソースコードを。
それに役立つことを願っています!