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

Настройка запросов авторизации и токенов с помощью клиента Spring Security 5.1

Настройка клиентских запросов OAuth2 в Spring Security 5

Автор оригинала: baeldung.

1. Обзор

Иногда API OAuth2 могут немного отличаться от стандартных, и в этом случае нам нужно сделать некоторые настройки для стандартных запросов OAuth2.

Spring Security 5.1 обеспечивает поддержку настройки авторизации OAuth2 и запросов токенов.

В этом руководстве мы рассмотрим, как настроить параметры запроса и обработку ответов.

2. Запрос на пользовательскую авторизацию

Во-первых, мы настроим запрос на авторизацию OAuth2. Мы можем изменять стандартные параметры и добавлять дополнительные параметры в запрос авторизации по мере необходимости.

Для этого нам необходимо реализовать наши собственные Распознаватель запросов авторизации OAuth2 :

public class CustomAuthorizationRequestResolver 
  implements OAuth2AuthorizationRequestResolver {
    
    private OAuth2AuthorizationRequestResolver defaultResolver;

    public CustomAuthorizationRequestResolver(
      ClientRegistrationRepository repo, String authorizationRequestBaseUri) {
        defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repo, authorizationRequestBaseUri);
    }
    
    // ...
}

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

Мы также переопределим методы resolve () , чтобы добавить нашу логику настройки:

public class CustomAuthorizationRequestResolver 
  implements OAuth2AuthorizationRequestResolver {

    //...

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
        OAuth2AuthorizationRequest req = defaultResolver.resolve(request);
        if(req != null) {
            req = customizeAuthorizationRequest(req);
        }
        return req;
    }

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
        OAuth2AuthorizationRequest req = defaultResolver.resolve(request, clientRegistrationId);
        if(req != null) {
            req = customizeAuthorizationRequest(req);
        }
        return req;
    }

    private OAuth2AuthorizationRequest customizeAuthorizationRequest(
      OAuth2AuthorizationRequest req) {
        // ...
    }

}

Мы добавим наши настройки позже, используя наш метод customize Authorization Request () , как мы обсудим в следующих разделах.

После реализации нашего обычая Распознаватель запросов авторизации OAuth2 , нам нужно добавить его в нашу конфигурацию безопасности:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.oauth2Login()
          .authorizationEndpoint()
          .authorizationRequestResolver(
            new CustomAuthorizationRequestResolver(
              clientRegistrationRepository(), "/oauth2/authorize-client"))
        //...
    }
}

Здесь мы использовали oauth2 Login().конечная точка авторизации().распознаватель запросов на авторизацию() чтобы ввести наш обычай Распознаватель запросов авторизации OAuth2.

3. Настройка Стандартные параметры запроса авторизации

Теперь давайте обсудим фактическую настройку. Мы можем изменить Запрос авторизации OAuth2 столько, сколько мы хотим.

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

Мы можем, например, сгенерировать свой собственный параметр “состояние” :

private OAuth2AuthorizationRequest customizeAuthorizationRequest(
  OAuth2AuthorizationRequest req) {
    return OAuth2AuthorizationRequest
      .from(req).state("xyz").build();
}

4. Дополнительные Параметры запроса авторизации

Мы также можем добавить дополнительные параметры в наш Запрос авторизации OAuth2 с использованием дополнительных параметров() метода OAuth2AuthorizationRequest и передачей Карты:

private OAuth2AuthorizationRequest customizeAuthorizationRequest(
  OAuth2AuthorizationRequest req) {
    Map extraParams = new HashMap();
    extraParams.putAll(req.getAdditionalParameters()); 
    extraParams.put("test", "extra");
    
    return OAuth2AuthorizationRequest
      .from(req)
      .additionalParameters(extraParams)
      .build();
}

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

Давайте рассмотрим более практический пример, настроив запрос на авторизацию, используемый с сервером авторизации Okta.

4.1. Пользовательский запрос авторизации Okta

Okta имеет дополнительные дополнительные параметры для запроса авторизации, чтобы предоставить пользователю больше возможностей. Например, idp , который указывает поставщика удостоверений.

Поставщик удостоверений по умолчанию в порядке, но мы можем настроить его с помощью параметра idp :

private OAuth2AuthorizationRequest customizeOktaReq(OAuth2AuthorizationRequest req) {
    Map extraParams = new HashMap();
    extraParams.putAll(req.getAdditionalParameters()); 
    extraParams.put("idp", "https://idprovider.com");
    return OAuth2AuthorizationRequest
      .from(req)
      .additionalParameters(extraParams)
      .build();
}

5. Пользовательский запрос токена

Теперь мы посмотрим, как настроить запрос токена OAuth2.

Мы можем настроить запрос токена, настроив Клиент ответа OAuth2 accessToken .

Реализация по умолчанию для OAuth2 accessToken Response Client is DefaultAuthorizationCodeTokenResponseClient .

Мы можем настроить сам запрос токена, предоставив пользовательский Конвертер сущностей запроса , и мы даже можем настроить обработку ответа токена, настроив DefaultAuthorizationCodeTokenResponseClient | RestOperations :

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.tokenEndpoint()
          .accessTokenResponseClient(accessTokenResponseClient())
            //...
    }

    @Bean
    public OAuth2AccessTokenResponseClient accessTokenResponseClient(){
        DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = 
          new DefaultAuthorizationCodeTokenResponseClient(); 
        accessTokenResponseClient.setRequestEntityConverter(new CustomRequestEntityConverter()); 

        OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = 
          new OAuth2AccessTokenResponseHttpMessageConverter(); 
        tokenResponseHttpMessageConverter.setTokenResponseConverter(new CustomTokenResponseConverter()); 
        RestTemplate restTemplate = new RestTemplate(Arrays.asList(
          new FormHttpMessageConverter(), tokenResponseHttpMessageConverter)); 
        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler()); 
        
        accessTokenResponseClient.setRestOperations(restTemplate); 
        return accessTokenResponseClient;
    }
}

Мы можем ввести свои собственные Клиент ответа OAuth2 accessToken с помощью конечная точка токена().клиент ответа на Токен доступа().

Чтобы настроить параметры запроса токенов, мы реализуем Custom Request Entity Converter. Аналогично, чтобы настроить обработку ответа на токен, мы реализуем Пользовательский конвертер ответов на токен.

Мы обсудим как Конвертер сущностей пользовательских запросов , так и Конвертер ответов пользовательских токенов в следующих разделах.

6. Дополнительные Параметры Запроса токена

Теперь мы посмотрим, как добавить дополнительные параметры в наш запрос токена, создав пользовательский Конвертер :

public class CustomRequestEntityConverter implements 
  Converter> {

    private OAuth2AuthorizationCodeGrantRequestEntityConverter defaultConverter;
    
    public CustomRequestEntityConverter() {
        defaultConverter = new OAuth2AuthorizationCodeGrantRequestEntityConverter();
    }
    
    @Override
    public RequestEntity convert(OAuth2AuthorizationCodeGrantRequest req) {
        RequestEntity entity = defaultConverter.convert(req);
        MultiValueMap params = (MultiValueMap) entity.getBody();
        params.add("test2", "extra2");
        return new RequestEntity<>(params, entity.getHeaders(), 
          entity.getMethod(), entity.getUrl());
    }

}

Наш Конвертер трансформирует Запрос на предоставление кода авторизации OAuth2 К Просьба.

Мы использовали конвертер по умолчанию OAuth2 Authorization Code Grant Request Entity Converter для обеспечения базовой функциональности и добавления дополнительных параметров в тело RequestEntity .

7. Обработка пользовательских Ответов на Токены

Теперь мы настроим обработку ответа токена.

Мы можем использовать конвертер ответов токенов по умолчанию OAuth2 accessToken Ответ HttpMessageConverter в качестве отправной точки.

Мы осуществим Пользовательский конвертер Ответов на токены чтобы справиться с “сфера применения” параметр по-разному:

public class CustomTokenResponseConverter implements 
  Converter, OAuth2AccessTokenResponse> {
    private static final Set TOKEN_RESPONSE_PARAMETER_NAMES = Stream.of(
        OAuth2ParameterNames.ACCESS_TOKEN, 
        OAuth2ParameterNames.TOKEN_TYPE, 
        OAuth2ParameterNames.EXPIRES_IN, 
        OAuth2ParameterNames.REFRESH_TOKEN, 
        OAuth2ParameterNames.SCOPE).collect(Collectors.toSet());

    @Override
    public OAuth2AccessTokenResponse convert(Map tokenResponseParameters) {
        String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);

        Set scopes = Collections.emptySet();
        if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
            String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
            scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, ","))
                .collect(Collectors.toSet());
        }

        //...
        return OAuth2AccessTokenResponse.withToken(accessToken)
          .tokenType(accessTokenType)
          .expiresIn(expiresIn)
          .scopes(scopes)
          .refreshToken(refreshToken)
          .additionalParameters(additionalParameters)
          .build();
    }

}

Преобразователь ответов токенов преобразует Map в Ответ OAuth2 accessToken.

В этом примере мы проанализировали параметр “scope” как разделенную запятыми вместо разделенной пробелом строки .

Давайте рассмотрим еще один практический пример, настроив ответ токена с помощью LinkedIn в качестве сервера авторизации.

7.1. Обработка ответов на токены LinkedIn

Наконец, давайте посмотрим, как обрабатывать ответ на токен LinkedIn . Это содержит только access_token и expires_in, но нам также нужен token_type.

Мы можем просто реализовать ваш собственный конвертер ответов токенов и установить token_type вручную:

public class LinkedinTokenResponseConverter 
  implements Converter, OAuth2AccessTokenResponse> {

    @Override
    public OAuth2AccessTokenResponse convert(Map tokenResponseParameters) {
        String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
        long expiresIn = Long.valueOf(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN));
        
        OAuth2AccessToken.TokenType accessTokenType = OAuth2AccessToken.TokenType.BEARER;

        return OAuth2AccessTokenResponse.withToken(accessToken)
          .tokenType(accessTokenType)
          .expiresIn(expiresIn)
          .build();
    }
}

8. Заключение

В этой статье мы узнали, как настроить авторизацию OAuth2 и запросы токенов, добавив или изменив параметры запроса.

Полный исходный код примеров доступен на GitHub .