ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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);
                }
        }
    }
    
    
    1. 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();
        }
    }
    
    1. 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;
        }
    }
    
    
    반응형
Designed by Tistory.