
OAuth2 인증 로그인 성공 및 실패를 위한 handler 설정
실패의 경우, 인증 시 사용했던 쿠키를 삭제하고, 요청 uri로 리다이렉트
@Component
@RequiredArgsConstructor
public class OAuth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private final CookieAuthorizationRequestRepository cookieAuthorizationRequestRepository;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException {
String targetUrl = CookieUtils.getCookie(request, CookieAuthorizationRequestRepository.REDIRECT_URI_PARAM_COOKIE_NAME)
.map(Cookie::getValue)
.orElse("/");
targetUrl = UriComponentsBuilder.fromUriString(targetUrl)
.queryParam("error", authenticationException.getLocalizedMessage())
.build().toUriString();
cookieAuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
}
성공의 경우, 타겟 URL (success redirect) 로 AccessToken 정보를 담아서 보내도록 설정, OAuth2인증 시 사용했던 권한 및 쿠키 정보 모두 삭제
ex) http://localhost:3000/user/redirect?token=<토큰정보>
위와 같은 형식으로, 클라이언트로 AccessToken이 넘어가게 됨.
따라서 프론트엔드와의 협의를 통한 targetUrl 설정 필수. 해당 url로 토큰이 넘어감
@Slf4j
@Component
@RequiredArgsConstructor
public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Value("${spring.security.oauth2.authorized-redirect-uris}")
private String redirectUri;
private final JwtTokenProvider jwtTokenProvider;
private final CookieAuthorizationRequestRepository cookieAuthorizationRequestRepository;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
String targetUrl = determineTargetUrl(request, response, authentication);
if (response.isCommitted()) {
return;
}
clearAuthenticationAttributes(request, response);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Optional<String> redirectUri = CookieUtils.getCookie(request, CookieAuthorizationRequestRepository.REDIRECT_URI_PARAM_COOKIE_NAME)
.map(Cookie::getValue);
if (redirectUri.isPresent() && !isAuthorizedRedirectUri(redirectUri.get())) {
throw new RuntimeException("redirect URIs are not matched.");
}
String targetUrl = redirectUri.orElse(getDefaultTargetUrl());
// JWT 생성
UserResponseDto.TokenInfo tokenInfo = jwtTokenProvider.generateToken(authentication);
// AccessToken return
return UriComponentsBuilder.
fromUriString(targetUrl)
.queryParam("token", tokenInfo.getAccessToken())
.build().toUriString();
}
// 권한 정보 삭제
protected void clearAuthenticationAttributes(HttpServletRequest request, HttpServletResponse response) {
super.clearAuthenticationAttributes(request);
cookieAuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
}
// uri 검증
private boolean isAuthorizedRedirectUri(String uri) {
URI clientRedirectUri = URI.create(uri);
URI authorizedUri = URI.create(redirectUri);
if (authorizedUri.getHost().equalsIgnoreCase(clientRedirectUri.getHost())
&& authorizedUri.getPort() == clientRedirectUri.getPort()) {
return true;
}
return false;
}
}