이메일 로그인 성공
→ accessToken, refreshToken: responseBody로 전달
소셜 로그인 성공
→ OAuth2SuccessHandler
→ accessToken: redirect query
→ refreshToken: cookie 저장
이메일을 통한 회원가입에선 응답 데이터로 accessToken, refreshToken을 전달했다. 소셜 로그인은 Spring Security를 이용하기 때문에 응답 데이터로 전달하기 어려웠다. 그래서 로그인 후 프론트로 리다이렉트할 때 쿼리 파라미터로 accessToken을 전달했다.
하지만 쿼리 파라미터로 전달할 시 보안에 취약하다는 단점이 있다. 1시간 단위로 만료되는 accessToken과 달리 refreshToken은 보안에 더 신경써야해서 쿠키를 사용했다.
쿠키를 생성/삭제하는 로직을 담은 CookieUtil을 생성한다.
public class CookieUtil {
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge){
Cookie cookie = new Cookie(name, value);
cookie.setPath("/");
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String name){
Cookie[] cookies = request.getCookies();
if (cookies== null) return;
for (Cookie cookie: cookies){
if (name.equals(cookie.getName())){
cookie.setValue("");
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
}
}
@PostMapping("/auth/logout")
public ApiResponse<Void> logout(@Valid @RequestBody TokenReissueDto dto,
HttpServletRequest request, HttpServletResponse response){
tokenService.logout(dto);
CookieUtil.deleteCookie(request, response, "refreshToken");
return ApiResponse.success(null);
}
"/logout"은 Spring Security가 예약해 둔 엔드포인트여서 "/auth/logout"으로 했다. 소셜 로그인의 경우 쿠키에도 refreshToken을 저장하므로, 컨트롤러에서 삭제해준다.
public void logout(TokenReissueDto dto){
String refreshTokenValue = dto.getRefreshToken();
RefreshToken refreshToken = refreshTokenRepository.findByRefreshToken(refreshTokenValue)
.orElseThrow(() -> new AuthException(AuthErrorCode.TOKEN_NOT_FOUND));
refreshTokenRepository.delete(refreshToken);
}
db에 저장된 refreshToken값을 삭제한다.
accessToken의 경우 db에 저장하지 않고 발급 즉시 프론트로 전달하여 프론트 localStorage에 저장되기에, 프론트 측에서 삭제하는 것으로 구현했다.