Рубрики
Без рубрики

Обновление тестов Spring Security OAuth и JUnit с помощью 👀 Java-хипстера

Узнайте, как использовать JUnit для тестирования ваших Java-приложений! Это расширенное руководство, в котором рассказывается об обновлении тестов и поддержке тестирования super hip в Spring Security и Spring Boot. С тегами java, учебник, webdev, безопасность.

Использование модульных и интеграционных тестов для проверки качества кода – отличный способ показать, что вы заботитесь о своем коде. Недавно я проделал большую работу в популярном проекте с открытым исходным кодом Hipster, чтобы обновить его для использования последней версии Spring Security.

Spring Security 5.1+ добавляет OAuth 2.0 и OIDC в качестве первоклассных граждан, которых вы можете настроить с помощью элегантного DSL (он же цепочка классных методов, он же. шаблон строителя). Я был мотивирован использовать его с тех пор, как Роб Уинч и его команда впервые запустили его. Было весело сотрудничать с ними в очень инновационном проекте. Весенняя безопасность делает OAuth потрясающим!

Я добавил поддержку OAuth 2.0 в JHipster осенью 2017 года. Этот опыт оказал на меня большое влияние. Я многое узнал о маскировке ключей, создании докеров и о том, как переключаться между поставщиками удостоверений личности (ВПЛ).

Я потратил последний месяц на обновление JHipster для использования Spring Security 5.1 (по умолчанию в Spring Boot 2.1). По пути я испытал некоторые разочарования, погрозил кулаком Трэвису СИ и обрадовался, когда нашел решение. Я также многому научился в этом процессе. Сегодня я собираюсь поделиться с вами этим опытом.

Выход из системы с помощью OAuth 2.0 и OIDC

Вскоре после интеграции поддержки Keycloak и Okta в JHipster проект получил множество жалоб от пользователей, которые не могли выйти из системы. Пользователи-хипстеры были знакомы с кликом Выход из системы (проверьте последнюю версию) и/|полностью вышел из системы. При поддержке безопасности Spring по умолчанию пользователи будут выходить из локального приложения, но не вынужденный переселенец.

Это заняло у меня год, но я, наконец, добавил глобальный выход из единого входа в начале этого года. Как Keycloak, так и Okta требуют, чтобы вы отправили запрос GET на конечную точку с маркером идентификатора и URL-адресом для перенаправления. Поэтому я создал Ресурс выхода , который возвращает эти значения.

@RestController
public class LogoutResource {
    private final Logger log = LoggerFactory.getLogger(LogoutResource.class);
    private final UserInfoRestTemplateFactory templateFactory;
    private final String accessTokenUri;

   public LogoutResource(UserInfoRestTemplateFactory templateFactory,
                         @Value("${security.oauth2.client.access-token-uri}") String accessTokenUri) {
       this.templateFactory = templateFactory;
       this.accessTokenUri = accessTokenUri;
   }

    /**
     * POST /api/logout : logout the current user
     *
     * @return the ResponseEntity with status 200 (OK) and a body with a global logout URL and ID token
     */
    @PostMapping("/api/logout")
    public ResponseEntity logout(HttpServletRequest request, Authentication authentication) {
        log.debug("REST request to logout User : {}", authentication);
        OAuth2RestTemplate oauth2RestTemplate = this.templateFactory.getUserInfoRestTemplate();
        String idToken = (String) oauth2RestTemplate.getAccessToken().getAdditionalInformation().get("id_token");

        String logoutUrl = accessTokenUri.replace("token", "logout");
        Map logoutDetails = new HashMap<>();
        logoutDetails.put("logoutUrl", logoutUrl);
        logoutDetails.put("idToken", idToken);
        request.getSession().invalidate();
        return ResponseEntity.ok().body(logoutDetails);
    }
}

Клиент Angular вызывает конечную точку /api/выход и создает URL-адрес выхода IdP.

this.authServerProvider.logout().subscribe(response => {
  const data = response.body;
  let logoutUrl = data.logoutUrl;
  // if Keycloak, uri has protocol/openid-connect/token
  if (logoutUrl.indexOf('/protocol') > -1) {
    logoutUrl = logoutUrl + '?redirect_uri=' + window.location.origin;
  } else {
    // Okta
    logoutUrl = logoutUrl + '?id_token_hint=' +
    data.idToken + '&post_logout_redirect_uri=' + window.location.origin;
  }
  window.location.href = logoutUrl;
});

Тестирование Ресурс выхода из системы был довольно простым. Основная часть работы заключалась в издевательстве над фабрикой userInfo RestTemplate таким образом, он вернул идентификационный токен.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = JhipsterApp.class)
public class LogoutResourceIntTest {

    @Autowired
    private MappingJackson2HttpMessageConverter jacksonMessageConverter;

    private final static String ID_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +
        ".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsIm" +
        "p0aSI6ImQzNWRmMTRkLTA5ZjYtNDhmZi04YTkzLTdjNmYwMzM5MzE1OSIsImlhdCI6MTU0M" +
        "Tk3MTU4MywiZXhwIjoxNTQxOTc1MTgzfQ.QaQOarmV8xEUYV7yvWzX3cUE_4W1luMcWCwpr" +
        "oqqUrg";

    @Value("${security.oauth2.client.access-token-uri}")
    private String accessTokenUri;

    private MockMvc restLogoutMockMvc;

    @Before
    public void before() {
        LogoutResource logoutResource = new LogoutResource(restTemplateFactory(), accessTokenUri);
        this.restLogoutMockMvc = MockMvcBuilders.standaloneSetup(logoutResource)
            .setMessageConverters(jacksonMessageConverter).build();
    }

    @Test
    public void getLogoutInformation() throws Exception {
        String logoutUrl = accessTokenUri.replace("token", "logout");
        restLogoutMockMvc.perform(post("/api/logout"))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
            .andExpect(jsonPath("$.logoutUrl").value(logoutUrl))
            .andExpect(jsonPath("$.idToken").value(ID_TOKEN));
    }

    private UserInfoRestTemplateFactory restTemplateFactory() {
        UserInfoRestTemplateFactory factory = mock(UserInfoRestTemplateFactory.class);
        Map idToken = new HashMap<>();
        idToken.put("id_token", ID_TOKEN);
        DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken("my-fun-token");
        token.setAdditionalInformation(idToken);
        when(factory.getUserInfoRestTemplate()).thenReturn(mock(OAuth2RestTemplate.class));
        when(factory.getUserInfoRestTemplate().getAccessToken()).thenReturn(token);
        return factory;
    }
}

Я объединил глобальную поддержку выхода в ветку Hipsters master в конце января и начал обновлять поддержку OIDC Spring Security несколько недель спустя.

Обновите поддержку OIDC Spring Security

Я начал с создания выпуска № 9276 для отслеживания своих целей, мотиваций и известных проблем.

На данный момент, если вы не очень хорошо знакомы с Spring Security, вам, вероятно, интересно: почему обновление до последней версии Spring Security так здорово ? Короче говоря: они устарели аннотации, добавили функции и упростили интеграцию OAuth 2.0 и OIDC в ваши приложения. Спасибо, команда весенней безопасности!

записка: Использование @enableoauth2sso и @Enableresourceserver больше не рекомендуется в Spring Boot 2.1+ (также известный как Spring Security 5.1+). Причины изменения можно найти в книге Джоша Лонга Полезный подкаст , опубликованный 25 января 2019 года. Это интервью с Мадхура Бхава и обсуждение начинается в 21:30.

В дополнение к преобразованию всего кода Java и конфигурации YAML для использования новейших бит безопасности Spring, я также решил сделать каждое приложение JHipster сервером ресурсов по умолчанию . Вот логика из шаблона JHipster SecurityConfiguration.java.ejs .:

@Override
public void configure(HttpSecurity http) throws Exception {
    // @formatter:off
    http
        ...
        <%_ } else if (authenticationType === 'oauth2') { _%>
            <%_ if (['monolith', 'gateway'].includes(applicationType)) { _%>
        .and()
            .oauth2Login()
            <%_ } _%>
        .and()
            .oauth2ResourceServer().jwt();
        <%_ } _%>
        // @formatter:on
  }
}

Чтобы убедиться, что реализация соответствует OIDC, я отменил значение по умолчанию JwtDecoder компонент с тем, который выполняет проверку аудитории.

@Value("${spring.security.oauth2.client.provider.oidc.issuer-uri}")
private String issuerUri;

@Bean
JwtDecoder jwtDecoder() {
    NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport)
        JwtDecoders.fromOidcIssuerLocation(issuerUri);

    OAuth2TokenValidator audienceValidator = new AudienceValidator();
    OAuth2TokenValidator withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
    OAuth2TokenValidator withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

    jwtDecoder.setJwtValidator(withAudience);

    return jwtDecoder;
}

После того, как у меня заработал весь код среды выполнения, я перешел к рефакторингу тестов. Тесты являются наиболее надежным показателем успеха рефакторинга, особенно в проекте, который имеет 26,000 комбинации, как это делает JHipster!

На этом пути я столкнулся с рядом проблем. Поскольку я многому научился, решая эти задачи, я подумал, что было бы забавно объяснить их и то, как я их решил.

Как имитировать аутентифицированного участника с помощью маркера идентификатора

Первая проблема, с которой я столкнулся, была связана с обновленным ресурсом Выхода из системы . Ниже приведен код после того, как я переработал его для использования репозитория регистрации клиентов Spring Security .

@RestController
public class LogoutResource {
    private ClientRegistration registration;

    public LogoutResource(ClientRegistrationRepository registrations) {
        this.registration = registrations.findByRegistrationId("oidc");
    }

    /**
     * {@code POST /api/logout} : logout the current user.
     *
     * @param request the {@link HttpServletRequest}.
     * @param idToken the ID token.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and a body with a global logout URL and ID token.
     */
    @PostMapping("/api/logout")
    public ResponseEntity logout(HttpServletRequest request,
                                    @AuthenticationPrincipal(expression = "idToken") OidcIdToken idToken) {
        String logoutUrl = this.registration.getProviderDetails()
            .getConfigurationMetadata().get("end_session_endpoint").toString();

        Map logoutDetails = new HashMap<>();
        logoutDetails.put("logoutUrl", logoutUrl);
        logoutDetails.put("idToken", idToken.getTokenValue());
        request.getSession().invalidate();
        return ResponseEntity.ok().body(logoutDetails);
    }
}

Я попытался использовать маркер аутентификации OAuth2 в LogoutResourceIT.java , , думая, что это приведет к заполнению Участника аутентификации

@RunWith(SpringRunner.class)
@SpringBootTest(classes = JhipsterApp.class)
public class LogoutResourceIT {

    @Autowired
    private ClientRegistrationRepository registrations;

    @Autowired
    private MappingJackson2HttpMessageConverter jacksonMessageConverter;

    private final static String ID_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +
        ".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsIm" +
        "p0aSI6ImQzNWRmMTRkLTA5ZjYtNDhmZi04YTkzLTdjNmYwMzM5MzE1OSIsImlhdCI6MTU0M" +
        "Tk3MTU4MywiZXhwIjoxNTQxOTc1MTgzfQ.QaQOarmV8xEUYV7yvWzX3cUE_4W1luMcWCwpr" +
        "oqqUrg";

    private MockMvc restLogoutMockMvc;

    @Before
    public void before() {
        LogoutResource logoutResource = new LogoutResource(registrations);
        this.restLogoutMockMvc = MockMvcBuilders.standaloneSetup(logoutResource)
            .setMessageConverters(jacksonMessageConverter).build();
    }

    @Test
    public void getLogoutInformation() throws Exception {

        Map claims = new HashMap<>();
        claims.put("groups", "ROLE_USER");
        claims.put("sub", 123);
        OidcIdToken idToken = new OidcIdToken(ID_TOKEN, Instant.now(),
            Instant.now().plusSeconds(60), claims);

        String logoutUrl = this.registrations.findByRegistrationId("oidc").getProviderDetails()
            .getConfigurationMetadata().get("end_session_endpoint").toString();
        restLogoutMockMvc.perform(post("/api/logout")
            .with(authentication(createMockOAuth2AuthenticationToken(idToken))))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
            .andExpect(jsonPath("$.logoutUrl").value(logoutUrl));
    }

    private OAuth2AuthenticationToken createMockOAuth2AuthenticationToken(OidcIdToken idToken) {
        Collection authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER));
        OidcUser user = new DefaultOidcUser(authorities, idToken);

        return new OAuth2AuthenticationToken(user, authorities, "oidc");
    }
}

Однако это привело к следующей ошибке:

Caused by: java.lang.IllegalArgumentException: tokenValue cannot be empty
    at org.springframework.util.Assert.hasText(Assert.java:284)
    at org.springframework.security.oauth2.core.AbstractOAuth2Token.(AbstractOAuth2Token.java:55)
    at org.springframework.security.oauth2.core.oidc.OidcIdToken.(OidcIdToken.java:53)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:172)

Я опубликовал эту проблему в StackOverflow и также отправил электронное письмо в службу безопасности Spring. Бабушка Джо ответила решением проблемы.

” То Аутентификацияпринципный решатель аргументов не регистрируется в вашем тесте.

ПРИМЕЧАНИЕ : Он автоматически регистрируется, когда включен “полный” spring-web-mvc, например @EnableWebMvc .

Однако, В вашем @Раньше , у вас есть:

Макет MockMvcBuilders.standaloneSetup() – это не инициализирует полную инфраструктуру web-mvc – только подмножество.

Попробуйте это вместо этого:

mockmvcbuilders.webappcontextsetup(этот.контекст) – это зарегистрирует Аутентификацияпринципный решатель аргументов и ваш тест должен разрешить Идентификационный Токен Oidc .”

Джо был прав. Я изменил тест на следующий, и тест прошел. ✅

@RunWith(SpringRunner.class)
@SpringBootTest(classes = JhipsterApp.class)
public class LogoutResourceIT {

    @Autowired
    private ClientRegistrationRepository registrations;

    @Autowired
    private WebApplicationContext context;

    private final static String ID_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +
        ".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsIm" +
        "p0aSI6ImQzNWRmMTRkLTA5ZjYtNDhmZi04YTkzLTdjNmYwMzM5MzE1OSIsImlhdCI6MTU0M" +
        "Tk3MTU4MywiZXhwIjoxNTQxOTc1MTgzfQ.QaQOarmV8xEUYV7yvWzX3cUE_4W1luMcWCwpr" +
        "oqqUrg";

    private MockMvc restLogoutMockMvc;

    @Before
    public void before() throws Exception {
        Map claims = new HashMap<>();
        claims.put("groups", "ROLE_USER");
        claims.put("sub", 123);
        OidcIdToken idToken = new OidcIdToken(ID_TOKEN, Instant.now(),
            Instant.now().plusSeconds(60), claims);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken(idToken));
        SecurityContextHolderAwareRequestFilter authInjector = new SecurityContextHolderAwareRequestFilter();
        authInjector.afterPropertiesSet();

        this.restLogoutMockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
    }

    @Test
    public void getLogoutInformation() throws Exception {
        String logoutUrl = this.registrations.findByRegistrationId("oidc").getProviderDetails()
            .getConfigurationMetadata().get("end_session_endpoint").toString();
        restLogoutMockMvc.perform(post("/api/logout"))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
            .andExpect(jsonPath("$.logoutUrl").value(logoutUrl))
            .andExpect(jsonPath("$.idToken").value(ID_TOKEN));
    }

    private OAuth2AuthenticationToken authenticationToken(OidcIdToken idToken) {
        Collection authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER));
        OidcUser user = new DefaultOidcUser(authorities, idToken);
        return new OAuth2AuthenticationToken(user, authorities, "oidc");
    }
}

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

Как передать токен доступа OAuth 2.0 нижестоящим микросервисам с помощью Zuul

JHipster использует Netflix Zuul для прокси-запросов от шлюза к нижестоящим микросервисам. Я создал Фильтр заголовка авторизации для обработки распространения маркера доступа.

public class AuthorizationHeaderFilter extends ZuulFilter {

    private final AuthorizationHeaderUtil headerUtil;

    public AuthorizationHeaderFilter(AuthorizationHeaderUtil headerUtil) {
        this.headerUtil = headerUtil;
    }

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        Optional authorizationHeader = headerUtil.getAuthorizationHeader();
        authorizationHeader.ifPresent(s -> ctx.addZuulRequestHeader(TokenRelayRequestInterceptor.AUTHORIZATION, s));
        return null;
    }
}

Однако добавление этого не привело к успешному распространению маркера доступа. С помощью Джона Радделла я обнаружил, что это произошло потому, что у JHipster был lazyinit BeanFactoryPostProcessor , из-за которого все бобы были загружены лениво. Инициализатор фильтра Zuul был включен в эту логику. Создание Инициализатора фильтра Zuul нетерпеливо загруженного компонента заставило все работать так, как раньше.

На данный момент у меня все работало, поэтому я создал запрос на обновление шаблонов для хипстеров .

Я знал, что то, что я зарегистрировал, требовало, чтобы для прохождения интеграционных тестов был запущен Keycloak. Это связано с обнаружением OIDC и тем, как конечные точки просматриваются из .хорошо известной/openid-конфигурации .

Как обрабатывать обнаружение OIDC в интеграционных тестах Spring Boot

Я не был слишком обеспокоен тем, что для прохождения интеграционных тестов необходимо было запустить Keycloak. Затем некоторые из наших сборок Azure и Travis начали выходить из строя. Разработчики JHipster отметили, что они видели ошибки, подобные приведенным ниже, когда Keycloak не работал.

Factory method 'clientRegistrationRepository' threw exception; nested exception isjava.lang.IllegalArgumentException: Unable to resolve the OpenID Configurationwith the provided Issuer of "http://localhost:9080/auth/realms/jhipster"

Я провел некоторые исследования с помощью тестов OAuth и OIDC Spring Security и нашел решение . Исправление включало добавление класса Конфигурация безопасности тестирования , который переопределяет параметры безопасности Spring по умолчанию и издевается над компонентами, чтобы обнаружение OIDC не происходило.

@TestConfiguration
public class TestSecurityConfiguration {
    private final ClientRegistration clientRegistration;

    public TestSecurityConfiguration() {
        this.clientRegistration = clientRegistration().build();
    }

    @Bean
    ClientRegistrationRepository clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(clientRegistration);
    }

    private ClientRegistration.Builder clientRegistration() {
        Map metadata = new HashMap<>();
        metadata.put("end_session_endpoint", "https://jhipster.org/logout");

        return ClientRegistration.withRegistrationId("oidc")
            .redirectUriTemplate("{baseUrl}/{action}/oauth2/code/{registrationId}")
            .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .scope("read:user")
            .authorizationUri("https://jhipster.org/login/oauth/authorize")
            .tokenUri("https://jhipster.org/login/oauth/access_token")
            .jwkSetUri("https://jhipster.org/oauth/jwk")
            .userInfoUri("https://api.jhipster.org/user")
            .providerConfigurationMetadata(metadata)
            .userNameAttributeName("id")
            .clientName("Client Name")
            .clientId("client-id")
            .clientSecret("client-secret");
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return mock(JwtDecoder.class);
    }

    @Bean
    public OAuth2AuthorizedClientService authorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {
        return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
    }

    @Bean
    public OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) {
        return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);
    }
}

Затем в классах, которые используют @SpringBootTest , я настроил это как источник конфигурации.

@SpringBootTest(classes = {MicroApp.class, TestSecurityConfiguration.class})

Выполнение сквозных тестов на микросервисах JHipster, защищенных с помощью OAuth 2.0

Вскоре после этого всплыл последний вопрос. jhipster-ежедневные сборки (запущенные в Azure DevOps) завершались неудачно, когда они пытались протестировать микросервисы.

Caused by: java.lang.IllegalArgumentException: Unable to resolve the OpenID Configuration with the provided Issuer of "http://localhost:9080/auth/realms/jhipster"

Мы не включаем в состав файлов настройки блокировки ключей для микросервисов, потому что мы не ожидаем, что они будут запускаться автономно. Для доступа к ним требуется шлюз, поэтому их настройки OAuth 2.0 должны соответствовать вашему шлюзу, а проект шлюза содержит файлы блокировки ключей.

Сквозные тесты, выполнявшиеся в Azure, где 1) запуск микросервиса и 2) попадание в конечную точку работоспособности, чтобы убедиться, что он успешно запущен. Чтобы исправить, Паскаль Гримо |/отключен запуск/тестирование микросервисов . Он также создал новую проблему для улучшения процесса, чтобы с помощью Hipsters JDL создавался полный стек микросервисов.

Обновление до версии Spring Security 5.1 и его Первоклассная поддержка OIDC

Я надеюсь, что этот список проблем и исправлений помог вам. Если вы используете устаревший @enableoauth2sso или @enableresourceserver , я рекомендую вам попробовать выполнить обновление до Spring Security 5.1. В проблеме, которую я использовал для отслеживания обновления , есть ссылки, которые показывают все необходимые изменения кода.

Используйте JHipster 6 для создания приложения Spring Boot + React с OIDC для аутентификации

Hipster 6 использует новейшие и лучшие версии Spring Boot и Spring Security. Он поддерживает Angular и реагирует на его внешний интерфейс. Он тоже поддерживает Vue , это просто не часть главного генератора.

Если вы создадите приложение с помощью JHipster 6, все функции тестирования, упомянутые в этом посте, будут в вашем приложении. Как ты это делаешь? Я рад, что вы спросили!

Начните с установки бета-версии JHipster 6:

npm install -g generator-jhipster@beta

записка: Команда npm является частью Node.js . Вам понадобится узел 10.x для установки JHipster и запуска полезных команд.

JHipster 6 поддерживает Java 8, 11 и 12 (благодаря Spring Boot 2.1). Я рекомендую управлять вашим Java SDK с помощью SDKMAN! Например, вы можете установить Java 12 и установить его по умолчанию.

sdk install java 12.0.0-open
sdk default java 12.0.0-open

Вы можете создать приложение JHipster, которое использует React и OIDC, всего несколькими командами:

mkdir app && cd app

echo "application { config { baseName reactoidc, \
  authenticationType oauth2, clientFramework react } }" >> app.jh

jhipster import-jdl app.jh

Ниже приведена запись терминала, которая показывает результаты выполнения этих команд.

Для успешного запуска приложения Spring Boot, созданного JHipster, должен быть запущен настроенный поставщик OIDC. Вы можете запустить маскировку ключей с помощью Docker Compose:

docker-compose -f src/main/docker/keycloak.yml up -d

Затем запустите свое приложение с помощью Maven:

./mvnw

Когда запуск завершится, откройте http://localhost:8080 , и нажмите войти . Вы будете перенаправлены на страницу Keycloak, где вы можете ввести admin/admin для входа в систему.

Почему Okta вместо Keycloak?

Блокировка ключей отлично работает, но это сообщение в блогах разработчиков Okta , так что позвольте мне показать вам, как вы можете использовать Okta! Почему вы должны использовать Okta? Это отличный вопрос.

Okta – это постоянно работающий поставщик удостоверений, который предоставляет услуги аутентификации и авторизации для разработчиков. Он также позволяет вам управлять своими пользователями. Мне нравится называть это Пользователями Как программный сервис, но UASS – не лучшая аббревиатура. Управление пользователями как программная услуга (UMASS) немного проще сходит с языка. В любом случае, это отличный сервис, и вы должны попробовать.

Зарегистрируйте Свое Приложение для Безопасной Загрузки Spring

Для начала зарегистрируйтесь в бесплатной учетной записи разработчика Okta ((или войдите в {ваш домен Okta} , если у вас уже есть учетная запись).

Как только вы войдете в Okta, зарегистрируйте свое приложение Spring Boot.

  • В верхнем меню нажмите на Приложения

  • Нажмите на Добавить приложение

  • Выберите Веб и нажмите Следующий

  • Введите Имя

  • Измените URL-адрес перенаправления входа на http://localhost:8080/login/oauth2/code/oidc

  • Нажмите Готово , затем Редактировать и добавьте http://localhost:8080 в качестве URL-адреса перенаправления для выхода из системы

  • Нажмите Сохранить

Ваши настройки должны напоминать снимок экрана ниже, когда вы закончите.

Создайте файл okta.env в корневом каталоге вашего проекта и замените значения {..} значениями из вашего приложения Okta:

export SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=https://{yourOktaDomain}/oauth2/default
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID={clientId}
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET={clientSecret}

совет: Добавить *.env в ваш .gitignore файл, чтобы этот файл не попал на GitHub.

Создавайте группы и добавляйте их в качестве утверждений к маркеру идентификатора

JHipster по умолчанию настроен для работы с двумя типами пользователей: администраторами и пользователями. Блокировка ключей настраивается для пользователей и групп автоматически, но вам необходимо выполнить некоторую одноразовую настройку для вашей организации Okta.

Создайте РОЛЕВОГО АДМИНИСТРАТОРА и РОЛЕВОЙ_ПОЛЬЗОВАТЕЛЬ группа ( Пользователи > Группы > Добавить группу ) и добавить в них пользователей. Вы можете использовать учетную запись, с которой вы зарегистрировались, или создать нового пользователя ( Пользователи > Добавить Человека ). Перейдите к API > Серверы авторизации и нажмите на сервер по умолчанию . Перейдите на вкладку Утверждения и Добавить утверждение . Назовите его группы и включите его в маркер идентификатора. Установите тип значения в Группы и установите фильтр в качестве регулярного выражения . * . Нажмите Создать .

Запустите приложение с помощью следующих команд:

source okta.env
./mvnw

Перейдите к http://localhost:8080 и используйте свои учетные данные Okta для входа в систему.

Довольно модно, тебе не кажется?! 🤓

Лучшее тестирование Java с помощью JHipster

JHipster создает для вас приложение с хорошим тестовым покрытием из коробки. Покрытие кода анализируется с помощью Облако сонара , которое автоматически настраивается для вас. Выполните следующую команду, чтобы запустить сонар в контейнере Docker.

docker-compose -f src/main/docker/sonar.yml up -d

Затем выполните следующую команду Maven:

./mvnw -Pprod clean test sonar:sonar -Dsonar.host.url=http://localhost:9001

Как только процесс завершится, перейдите к http://localhost:9001/projects и вы увидите отчет о своем проекте.

записка: Охват кода намного выше, чем показано в этом отчете. Недавно мы изменили многие тесты для запуска на этапе интеграционного тестирования и не выяснили, как сообщить эти данные в Sonar.

См. Документация по качеству кода Hipster для получения дополнительной информации об этой функции.

Поддержка JUnit 5 в JHipster также находится в разработке .

Узнайте больше о Spring Security, Spring Boot и хипстере

Я надеюсь, вам понравилась моя история об обновлении Hipster до использования Spring Security 5.1 и его звездного OAuth 2.0+ Поддержка OIDC. Мне очень нравится то, что сделала команда Spring Security, чтобы упростить ее конфигурацию и сделать обнаружение OIDC (среди прочего) простым.

Я не создавал репозиторий GitHub для этого примера, так как JHipster сгенерировал весь код, и мне не нужно было ничего изменять.

Если вы хотите узнать больше о Hipster 6, посмотрите Лучшую, более быструю и легкую Java с Java 12 и JHipster 6. Если вас интересуют возможности создания Jhipster CRUD и поддержка PWA, я рекомендую вам ознакомиться с моим сообщением в блоге о том, как создать фотогалерею PWA с помощью React, Spring Boot и JHipster.

Мы также опубликовали ряд сообщений о тестировании и безопасности Spring 5.1:

Хотите больше технических советов? Следуйте за нами в социальных сетях { Twitter , LinkedIn , Facebook , YouTube }, чтобы получать уведомления, когда мы публикуем новый контент.

У вас есть вопрос по поводу Okta, который не связан с этим сообщением? Пожалуйста, спросите об этом на нашем форумы разработчиков .

Оригинал: “https://dev.to/oktadev/upgrading-spring-security-oauth-and-junit-tests-through-the-of-a-java-hipster-bn3”