1. 개요
Spring Security 5.1은 OAuth2 권한 부여 및 토큰 요청을 사용자 정의하기 위한 지원을 제공합니다.
이 사용방법(예제)에서는 요청 매개변수와 응답 처리를 사용자 지정하는 방법을 살펴봅니다.2. Custom 승인 요청
를 구현해야 합니다 .
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 요청 추가 매개변수
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();
}
또한 새 매개변수를 추가하기 전에 이전 추가
매개변수를 포함해야 합니다.4.1. Autohirziation 지정 Okta 승인 요청
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. 커스텀 토큰 요청
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 토큰 응답 처리
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();
}
}