どのように@PreAuthorizedでannoted春ブートコントローラのメソッドをテストすることができます(hasAnyAuthority(...))

モーセBesong:

私のコントローラクラスは、以下であります:

 @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注釈付きのコントローラのメソッドメソッドのテストを書くべきことができますか?

Ruaroティボー:

私はこの問題を解決する方法を考え出すの日の部分を費やしてきました。私はそんなに悪くないと思う、と多くを助けることができるという解決策になってしまいました。

あなたがテストで実行しようとしました何に基づいて、あなたが行うことができ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"));
    }
}

私は、このソリューションのおかげに思い付きました:

好奇心のために:
春のロード、テストする場合InMemoryTokenStore、およびあなたが(として提供する1つを取る可能性を与えます@Bean)。生産で実行している場合は、私の場合のために、春が使用するRemoteTokenStoreトークンをチェックするために、リモート認証サーバを呼び出します、( http://authorization_server/oauth/check_token)。
あなたがのOAuth2を使用することを決定したとき、春は発射しますOAuth2AuthenticationProcessingFilterこれは、すべて私のデバッグセッション中に、私のエントリポイントでした。

私はこのためにあなたに感謝し、多くのことを学びました。
あなたは見つけることができ、ここでソースコードを

それに役立つことを願っています!

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=364072&siteId=1