1. 개요

 
때로는 OAuth2 API가 표준에서 약간 다를 수 있으며, 이 경우 표준 OAuth2 요청에 대해 몇 가지 사용자 정의를 수행해야 합니다.

Spring Security 5.1은 OAuth2 권한 부여 및 토큰 요청을 사용자 정의하기 위한 지원을 제공합니다.

이 사용방법(예제)에서는 요청 매개변수와 응답 처리를 사용자 지정하는 방법을 살펴봅니다.

2. Custom 승인 요청

 
먼저 OAuth2 승인 요청을 사용자 정의합니다. 필요에 따라 표준 매개변수를 수정하고 권한 부여 요청에 추가 매개변수를 추가할 수 있습니다.그렇게 하려면 자체 OAuth2AuthorizationRequestResolver

를 구현해야 합니다  .

public class CustomAuthorizationRequestResolver 
  implements OAuth2AuthorizationRequestResolver {
    
    private OAuth2AuthorizationRequestResolver defaultResolver;

    public CustomAuthorizationRequestResolver(
      ClientRegistrationRepository repo, String authorizationRequestBaseUri) {
        defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repo, authorizationRequestBaseUri);
    }
    
    // ...
}
기본 기능을 제공하기 위해

DefaultOAuth2AuthorizationRequestResolver

를 사용했습니다  .또한

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) {
        // ...
    }

}
 다음 섹션에서 논의할 것처럼 사용자 정의를 나중에 

customAuthorizationRequest() 메소드를 사용하여 추가할 것입니다.

사용자 정의 OAuth2AuthorizationRequestResolver

를 구현한 후  Security 구성에 추가해야 합니다.
@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.oauth2Login()
          .authorizationEndpoint()
          .authorizationRequestResolver(
            new CustomAuthorizationRequestResolver(
              clientRegistrationRepository(), "/oauth2/authorize-client"))
        //...
    }
}

여기에서  oauth2Login().authorizationEndpoint().authorizationRequestResolver() 를 사용하여 사용자 지정  OAuth2AuthorizationRequestResolver를 삽입했습니다.

3.  승인 요청 표준 매개변수 사용자 정의

 
이제 실제 사용자 정의에 대해 논의해 보겠습니다.

OAuth2AuthorizationRequest

를 원하는 만큼 수정할 수 있습니다  .우선

각 승인 요청에 대한 표준 매개변수를 수정할 수 있습니다.

예를 들어 다음과 같이 자체

"상태"

매개변수를 생성할 수 있습니다.
private OAuth2AuthorizationRequest customizeAuthorizationRequest(
  OAuth2AuthorizationRequest req) {
    return OAuth2AuthorizationRequest
      .from(req).state("xyz").build();
}

4. Authorization 요청  추가 매개변수

 
OAuth2AuthorizationRequest 의

additionalParameters()

메서드를 사용하고 맵을 전달하여

OAuth2AuthorizationRequest

매개 변수를 추가할 수도 있습니다 

.

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

또한 새 매개변수를 추가하기 전에 이전 추가

매개변수를 포함해야 합니다.
Okta Authorization Server와 함께 사용되는 권한 요청을 사용자 지정하여 보다 실용적인 예를 살펴보겠습니다.

4.1. Autohirziation 지정 Okta 승인 요청

 

Okta

에는 사용자에게 더 많은 기능을 제공하기 위해 권한 부여 요청에 대한 추가 선택적 매개변수가 있습니다. 예를 들어  ID 공급자를 나타내는

idp 입니다.

ID 제공자는 기본적으로 Okta이지만 idp

매개변수 를 사용하여 사용자 정의할 수 있습니다 .
private OAuth2AuthorizationRequest customizeOktaReq(OAuth2AuthorizationRequest req) {
    Map<String,Object> extraParams = new HashMap<String,Object>();
    extraParams.putAll(req.getAdditionalParameters()); 
    extraParams.put("idp", "https://idprovider.com");
    return OAuth2AuthorizationRequest
      .from(req)
      .additionalParameters(extraParams)
      .build();
}

5. 커스텀 토큰 요청

 
이제 OAuth2 토큰 요청을 사용자 지정하는 방법을 살펴보겠습니다.

OAuth2AccessTokenResponseClient 를 사용자 정의하여 토큰 요청을 사용자 정의할 수 있습니다  .

OAuth2AccessTokenResponseClient

의 기본 구현  은 

DefaultAuthorizationCodeTokenResponseClient

입니다.

사용자 지정 RequestEntityConverter

를 제공하여 토큰 요청 자체를 사용자 지정할 수 있으며

DefaultAuthorizationCodeTokenResponseClient RestOperations

를 사용자 지정하여 토큰 응답 처리를 사용자 지정할 수도 있습니다  .
@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.tokenEndpoint()
          .accessTokenResponseClient(accessTokenResponseClient())
            //...
    }

    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> 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;
    }
}

tokenEndpoint().accessTokenResponseClient() 를 사용하여  자체 OAuth2AccessTokenResponseClient  를 주입할 수 있습니다  .

토큰 요청 매개변수를 사용자 정의하기 위해

CustomRequestEntityConverter를 구현합니다.

마찬가지로 토큰 응답 처리를 사용자 지정하기 위해 

CustomTokenResponseConverter를 구현합니다.

다음 섹션에서

CustomRequestEntityConverter

와 

CustomTokenResponseConverter

에 대해  설명합니다.

6. 토큰 요청 추가 매개변수

 

이제 사용자 정의 Converter

를 빌드하여 토큰 요청에 추가 매개변수를 추가하는 방법을 살펴보겠습니다 .
public class CustomRequestEntityConverter implements 
  Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> {

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

}

변환기 는 OAuth2AuthorizationCodeGrantRequest RequestEntity  로 변환  합니다 .

기본 변환기 

OAuth2AuthorizationCodeGrantRequestEntityConverter 를 사용하여 기본 기능을 제공하고

RequestEntity

본문 에 추가 매개변수를 추가했습니다  .

7. 사용자 정의 토큰 응답 처리

 
이제 토큰 응답 처리를 사용자 정의합니다.기본 토큰 응답 변환기 

인 OAuth2AccessTokenResponseHttpMessageConverter

를 시작점으로 사용할 수 있습니다.

"scope" 매개변수를 다르게 처리하기 위해  CustomTokenResponseConverter 를 구현  합니다.

public class CustomTokenResponseConverter implements 
  Converter<Map<String, String>, OAuth2AccessTokenResponse> {
    private static final Set<String> 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<String, String> tokenResponseParameters) {
        String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);

        Set<String> 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

을 

OAuth2AccessTokenResponse로 변환합니다.

이 예에서는 공백으로 구분된 문자열 대신 쉼표로 구분된

"범위" 매개변수를 구문 분석했습니다.

LinkedIn을 인증 서버로 사용하여 토큰 응답을 사용자 지정하여 또 다른 실제 예를 살펴보겠습니다.

7.1. LinkedIn 토큰 응답 처리

 

마지막으로 LinkedIn

토큰 응답 을 처리하는 방법을 살펴보겠습니다  . 여기에는

access_token

expires_in 만 포함되지만

token_type 도 필요 합니다.자체 토큰 응답 변환기를 구현하고

token_type을

수동으로 설정할 수 있습니다.
public class LinkedinTokenResponseConverter 
  implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {

    @Override
    public OAuth2AccessTokenResponse convert(Map<String, String> 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에서

사용할 수 있습니다 .
Security footer banner