source

oAuth2 리소스 서버 애플리케이션 내에서 @WithMock 사용자(@SpringBootTest 포함) 사용

itover 2023. 7. 16. 19:26
반응형

oAuth2 리소스 서버 애플리케이션 내에서 @WithMock 사용자(@SpringBootTest 포함) 사용

환경:여러 인프라 서비스와 리소스 서비스(비즈니스 로직 포함)로 구성된 스프링 부트 기반 마이크로서비스 아키텍처 애플리케이션을 보유하고 있습니다.권한 부여 및 인증은 사용자 엔티티를 관리하고 클라이언트에 대한 JWT 토큰을 생성하는 oAuth2-Service에 의해 처리됩니다.

단일 마이크로서비스 애플리케이션 전체를 테스트하기 위해 testNG, spring.boot.test, org.springframework로 테스트를 구축하려고 했습니다.보안.테스트...

@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


질문:제가 잘못하고 있다는 것이 완전히 명백한 것이 있습니까?또는 @WithMock 사용자가 @SpringBoot과 함께 작동하지 않을 것입니다.oAuth2 환경에서 테스트 및 @AutoConfigureMockMvc?이런 경우라면...이와 같은 (통합) 테스트의 일부로 경로 및 방법 기반 보안 구성을 테스트하기 위한 가장 좋은 접근 방식은 무엇입니까?


부록: 저는 다음과 같은 다양한 접근법을 시도했습니다.하지만 그것은 같은 결과로 이어졌습니다 :(

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

참조:
춘계 보안 시험
스프링 부트 1.4 테스트

@WithMockUserSecurityContext에 인증을 만듭니다.에도 동일하게 적용됩니다.with(user("username")).

기본적으로 OAuth2Auth2AuthenticationProcessingFilter는 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로 설정합니다.

저는 동일한 문제가 있었고, 토큰을 만들어 모의 MVC 수행에 사용하는 방법밖에 없었습니다.

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

그리고 OA의 조력자:

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

언급URL : https://stackoverflow.com/questions/41824885/use-withmockuser-with-springboottest-inside-an-oauth2-resource-server-applic

반응형