이번게시글에 다룰 내용은 redirect_uri를 처리할 수 있는 Controller 또는 AuthenticationFilter를 직접 구현하여 서비스측의 인가 처리를 하는 과정이다. 무엇을 이야기하는 건지 잘 모르는 것이 정상이다. 아래 kakao Dev Talk을 읽고 오는 것을 추천한다.
Rest api와 javascript Spring Oauth2 카카오 로그인 시 302 리다이렉트 관련 문의
해당 문제를 해결하는 내용을 게시글에 작성할 예정이다.
Spring Security Oauth2 Client의 동작 방식을 자세하게 정리해둔 게시글이다. 비록 나는 혼자 Spring Security의 동작 방식을 혼자 디버깅하면서 공부하고 발견한 게시글이지만, 처음 접한 사람은 이 게시글을 읽고 시작하는 것을 추천한다.
나는 카카오에서 추천한대로 redirect_uri를 처리할 수 있는 AuthenticationFilter를 구현하여 문제를 해결하였다. 내가 구현한 AuthenticationFilter는 아래와 같다.
@RequiredArgsConstructor
public class PreOAuth2AuthorizationRequestFilter extends OncePerRequestFilter {
private final ClientRegistrationRepository clientRegistrationRepository;
private final AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String requestURI = request.getRequestURI();
String state = request.getParameter(OAuth2ParameterNames.STATE);
if (KAKAO_REDIRECT_URI.equals(requestURI) && IOS_STATE.equals(state)) {
OAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest(request, state);
if (authorizationRequest != null) {
authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
} else {
throw new KakaoOauth2LoginFailureException("Failed to process OAuth2 authorization request");
}
}
filterChain.doFilter(request, response);
}
private OAuth2AuthorizationRequest createAuthorizationRequest(HttpServletRequest request, String state) {
ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId(KAKAO_REGISTRATION_ID);
if (clientRegistration == null) {
return null;
}
OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode()
.clientId(clientRegistration.getClientId())
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
.redirectUri(expandRedirectUri(request, clientRegistration, "login"))
.scopes(clientRegistration.getScopes())
.state(state)
.attributes(attributes -> {
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, KAKAO_REGISTRATION_ID);
});
return builder.build();
}
private static String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration, String action) {
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(request.getContextPath())
.replaceQuery(null)
.fragment(null)
.build();
String scheme = uriComponents.getScheme();
uriVariables.put("baseScheme", (scheme != null) ? scheme : "");
String host = uriComponents.getHost();
uriVariables.put("baseHost", (host != null) ? host : "");
int port = uriComponents.getPort();
uriVariables.put("basePort", (port == -1) ? "" : ":" + port);
String path = uriComponents.getPath();
if (StringUtils.hasLength(path)) {
if (path.charAt(0) != '/') {
path = '/' + path;
}
}
uriVariables.put("basePath", (path != null) ? path : "");
uriVariables.put("baseUrl", uriComponents.toUriString());
uriVariables.put("action", (action != null) ? action : "");
return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri())
.buildAndExpand(uriVariables)
.toUriString();
}
}
DefaultOAuth2AuthorizationRequestResolver를 참고하여 구현하였으며, 로직 자체는 간단하다. 파라미터 중 "state" 값이 "ios"라면 AuthorizationRequestRepository에 kakao에 관련한 OAuth2AuthorizationRequest를 ClientRegistrationRepository를 이용하여 생성한뒤, 넣어주면 된다.
카카오 소셜 간편 로그인(javascript SDK) + spring oauth2 security 적용기
필터를 이용한 방식이 아닌, 쿠키를 이용한 해결방식도 있으니 참고해서 자신에게 맞는 방식을 적용해서 해결하기를 추천한다.