-
OAuth2 AccessTokenResponseHandler Customize카테고리 없음 2024. 2. 29. 20:42
respons handler 는
OAuth2TokenEndpointFilter 에 정의되어있다.
private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication; OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken(); OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken(); Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters(); OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()) .tokenType(accessToken.getTokenType()) .scopes(accessToken.getScopes()); if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) { builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt())); } if (refreshToken != null) { builder.refreshToken(refreshToken.getTokenValue()); } if (!CollectionUtils.isEmpty(additionalParameters)) { builder.additionalParameters(additionalParameters); } OAuth2AccessTokenResponse accessTokenResponse = builder.build(); ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse); }
OAuth2TokenEndpointFilter 에 있는 AuthenticationSuccessHandler 는 OAuth2AccessTokenResponseHttpMessageConverter 로 response write 작업을 한다.
OAuth2AccessTokenResponseHttpMessageConverter 에 write 에 custom response 를 넣을 수 있게 customize 한 messageConverter 를 넣어줘야한다.
AbstractHttpMessageConverter<AccessTokenResponse> 를 상속한 customs converter 를 생성해야한다. (this.accessTokenHttpResponseConverter 대체)
해당 구현체는 readInternal, writeInternal 을 구현해야한다.
readInternal 에는 HttpInputMessage를 map으로 바꿔 accessTokenResponseConverter를 통해 tokenResponse객체로 교체하고,
writeInternal 에서는 tokenResponse 객체를 converter 로 map으로 변경한다.
OAuth2AccessTokenResponseHttpMessageConverter 에서는
- read 는 DefaultMapOAuth2AccessTokenResponseConverter 를
- write 는 DefaultOAuth2AccessTokenResponseMapConverter 를 사용한다.
이 converter 들도 spring 이 만든 OAuth2AccessTokenResponse 를 대상으로 하기 때문에 custom 하게 만들어줘야한다.
OAuth2TokenEndpointFilter 에 있는 onAuthenticationSuccess 메서드를 구현
@RequiredArgsConstructor public class AccessTokenResponseHandler implements AuthenticationSuccessHandler { /* private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();*/ private final HttpMessageConverter<AccessTokenResponse> accessTokenHttpResponseConverter = new AccessTokenResponseHttpConverter(); @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication; long between = 0; OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken(); if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) { between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()); } AccessTokenResponse accessTokenResponse = new AccessTokenResponse( accessToken.getTokenValue(), accessToken.getTokenType().getValue(), between ); ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse); } }
/** * OAuth2AccessTokenResponseHttpMessageConverter * */ public class AccessTokenResponseHttpConverter extends AbstractHttpMessageConverter<AccessTokenResponse> { private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; private MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter(); private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<Map<String, Object>>() { }; private final Converter<Map<String, Object>, AccessTokenResponse> accessTokenResponseConverter = new CustomMapOAuth2AccessTokenResponseConverter(); private Converter<AccessTokenResponse, Map<String, Object>> accessTokenResponseParametersConverter = new CustomAccessTokenResponseMapConverter(); public AccessTokenResponseHttpConverter() { super(DEFAULT_CHARSET, MediaType.APPLICATION_JSON, new MediaType("application", "*+json")); } @Override protected boolean supports(Class<?> clazz) { return AccessTokenResponse.class.isAssignableFrom(clazz); } @Override protected AccessTokenResponse readInternal(Class<? extends AccessTokenResponse> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { try { Map<String, Object> tokenResponseParameters = (Map<String, Object>) this.jsonMessageConverter .read(STRING_OBJECT_MAP.getType(), null, inputMessage); return this.accessTokenResponseConverter.convert(tokenResponseParameters); } catch (Exception ex) { throw new HttpMessageNotReadableException( "An error occurred reading the OAuth 2.0 Access Token Response: " + ex.getMessage(), ex, inputMessage); } } @Override protected void writeInternal(AccessTokenResponse accessTokenResponse, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { try { Map<String, Object> tokenResponseParameters = this.accessTokenResponseParametersConverter .convert(accessTokenResponse); this.jsonMessageConverter.write(tokenResponseParameters, STRING_OBJECT_MAP.getType(), MediaType.APPLICATION_JSON, outputMessage); } catch (Exception ex) { throw new HttpMessageNotWritableException( "An error occurred writing the OAuth 2.0 Access Token Response: " + ex.getMessage(), ex); } } }
- read 의 converter
/** * public final class DefaultMapOAuth2AccessTokenResponseConverter * implements Converter<Map<String, Object>, OAuth2AccessTokenResponse> * */ public class CustomMapOAuth2AccessTokenResponseConverter implements Converter<Map<String, Object>, AccessTokenResponse>{ @Override public AccessTokenResponse convert(Map<String, Object> source) { String accessToken = source.get(OAuth2ParameterNames.ACCESS_TOKEN).toString(); String tokenType = source.get(OAuth2ParameterNames.TOKEN_TYPE).toString(); long expiresIn = Long.parseLong(source.get(source).toString()); return AccessTokenResponse.builder() .tokenValue(accessToken) .tokenType(tokenType) .expiresIn(expiresIn) .build(); } }
- write converter
/** * public final class DefaultOAuth2AccessTokenResponseMapConverter * implements Converter<OAuth2AccessTokenResponse, Map<String, Object>> { * */ public class CustomAccessTokenResponseMapConverter implements Converter<AccessTokenResponse, Map<String, Object>> { @Override public Map<String, Object> convert(AccessTokenResponse tokenResponse) { Map<String, Object> parameters = new HashMap<>(); parameters.put(OAuth2ParameterNames.ACCESS_TOKEN, tokenResponse.getAccessToken()); parameters.put(OAuth2ParameterNames.TOKEN_TYPE, tokenResponse.getTokenType()); parameters.put(OAuth2ParameterNames.EXPIRES_IN, tokenResponse.getExpiresIn()); return parameters; } }
반응형