Удаленный JWT сервис Mock Spring - PullRequest
0 голосов
/ 03 января 2019

В настоящее время я использую RemoteTokenServices класс:

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Value("${auth-server.url}")
    private String authEndpoint;

    @Value("${security.oauth2.client.client-id}")
    private String clientId;

    @Value("${security.oauth2.client.client-secret}")
    private String clientSecret;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("ms/legacy");
    }

    @Bean
    public ResourceServerTokenServices tokenService() {
        RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setClientId(clientId);
        tokenServices.setClientSecret(clientSecret);
        tokenServices.setCheckTokenEndpointUrl(authEndpoint + "/uaa/oauth/check_token");
        return tokenServices;
    }
}

Я хочу иметь возможность легко и правильно смоделировать это для всех моих интеграционных тестов конечных точек, зная, что:

  • JWT декодируется в OncePerRequestFilter для получения важной информации
  • Меня не интересует тестирование сбоев аутентификации (да, но это не то, что мы хотим сделать на каждой конечной точке)

Существует ли стандартный способ:

  1. Создать токен JWT вручную?
  2. Легко проверять доступ ко всем службам токенов?

Ожидаемым результатом будет то, что я смогу написать тест конечной точки с несколькими дополнительными строками для установки правильного JWT в запросе, и служба токенов тупо согласится с его достоверностью.

1 Ответ

0 голосов
/ 17 января 2019

Учитывая то, что мы вообще не хотим тестировать безопасность, лучшее решение для такого рода случаев заключается в следующем:

  • использовать стандартные средства управления безопасностью Spring * @WithMockUser вместе с MockMvc
  • Адаптируйте ResourceServerConfigurerAdapter для тестов:
  • Создайте базовый класс, содержащий все конфигурации, кроме токенов
  • Создайте наследующий класс для профилей, не относящихся к тестам (@ActiveProfiles("!test")) в которой размещается конфигурация, специфичная для токена
  • создает наследующий класс для тестового профиля, который отключает удаленную проверку токена (security.stateless(false);)
  • , чтобы тестовые классы использовали test профиль
  • Внедрить правильную информацию, извлеченную из токенов, в нужное время в тестах

Вот как это было реализовано на практике:

База ResourceServerConfigurerAdapter, чтобы конфигурация имелаОсновная общая часть между контекстами тестов и не-тестов:

public class BaseResourceServerConfiguration extends ResourceServerConfigurerAdapter {

  @Override
  public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources.resourceId("ms/legacy");
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().permitAll().and().cors().disable().csrf().disable().httpBasic().disable()
        .exceptionHandling()
        .authenticationEntryPoint(
            (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
        .accessDeniedHandler(
            (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED));
  }

}

Его реализация вне для не-тестов:

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Profile("!test")
public class ResourceServerConfiguration extends BaseResourceServerConfiguration {

    @Value("${auth-server.url}")
    private String authEndpoint;

    @Value("${security.oauth2.client.client-id}")
    private String clientId;

    @Value("${security.oauth2.client.client-secret}")
    private String clientSecret;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("ms/legacy");
    }

    @Bean
    public ResourceServerTokenServices tokenService() {
        RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setClientId(clientId);
        tokenServices.setClientSecret(clientSecret);
        tokenServices.setCheckTokenEndpointUrl(authEndpoint + "/uaa/oauth/check_token");
        return tokenServices;
    }
}

И для тестов:

@Configuration
@EnableResourceServer
@ActiveProfiles("test")
public class TestResourceServerConfigurerAdapter extends BaseResourceServerConfiguration {

  @Override
  public void configure(ResourceServerSecurityConfigurer security) throws Exception {
    super.configure(security);

    // Using OAuth with distant authorization service, stateless implies that the request tokens
    // are verified each time against this service. In test, we don't want that because we need
    // properly isolated tests. Setting this implies that the security is checked only locally
    // and allows us to mock it with @WithMockUser, @AutoConfigureMockMvc and autowired MockMVC
    security.stateless(false);
  }

}

Введите информацию о токене с помощью фильтра запросовr тесты:

@Component
@ActiveProfiles("test")
public class TestRequestFilter extends OncePerRequestFilter {

  private Optional<InfoConf> nextInfoConf = Optional.empty();

  // Request info is our request-scoped bean that holds JWT info
  @Autowired
  private RequestInfo info;

  @Override
  protected void doFilterInternal(HttpServletRequest httpServletRequest,
      HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
    if (nextInfoConf.isPresent()) {
      info.setInfoConf(nextInfoConf.get());
    }
    filterChain.doFilter(httpServletRequest, httpServletResponse);
  }

  public void setNextInfoConf(InfoConf nextInfoConf) {
    this.nextInfoConf = Optional.of(nextInfoConf);
  }

  public void clearNextInfoConf() {
    nextInfoConf = Optional.empty();
  }

}

И, конечно, заставить анализ JWT ничего не делать, когда нет JWT.

Мы также написали небольшой служебный компонент для создания соответствующей информации для внедрения.

Типичный интеграционный тест будет выглядеть следующим образом:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class TestClass {

    @Autowired
    protected MockMvc mockMvc;

    @Before
    public void before() {
        // Create an user in DB
        // Inject the related information in our filter
    }

    @After
    public void after() {
        // Cleanup both in DB and filter
    }

    @Test
    @WithMockUser
    public void testThing() throws Exception {
        // Use MockMVC
    }
}

Другое решение - это действительно смоделировать ResourceServerTokenServices, но на самом деле гораздо сложнее создать правильные токены и использовать стандартный макет безопасности Spring.кажется гораздо более уместным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...