From 0cc060414ed206eb5c946d9b95900ea46e902317 Mon Sep 17 00:00:00 2001 From: jeonghyemin Date: Mon, 23 Jun 2025 16:39:42 +0900 Subject: [PATCH 1/3] =?UTF-8?q?refactor(user):=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC=20=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/OAuth2LoginSuccessHandler.java | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java b/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java index 60ce2a6..fc9aeae 100644 --- a/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java +++ b/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -28,7 +29,6 @@ // 1. JWT 토큰 발급 // - 이때, JWT payload는 보안상 최소한의 정보(userId, role)만 담겠다 // 2. refreshToken만 DB에 저장 -// 3. JSON 응답으로, accessToken과 refreshToken 을 반환해준다. @Component @RequiredArgsConstructor @Slf4j @@ -40,42 +40,30 @@ public class OAuth2LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHan public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - // 1. CustomOAuth2UserService에서 설정한 OAuth2User 정보 가져오기 - CustomOAuth2User customUserDetails = (CustomOAuth2User)authentication.getPrincipal(); - + CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal(); User user = customUserDetails.getUser(); Long userId = customUserDetails.getUserId(); - String email = customUserDetails.getName(); - - Collection authorities = authentication.getAuthorities(); - Iterator iterator = authorities.iterator(); - GrantedAuthority auth = iterator.next(); - - String role = auth.getAuthority(); + String role = authentication.getAuthorities().iterator().next().getAuthority(); - log.info("user, userId, email, role :: {} {} {} {}", user, userId, email, role); + // JWT 발급 + String accessToken = jwtUtil.createAccessToken("accessToken", userId, role, 30 * 60 * 1000L); // 30분 + String refreshToken = jwtUtil.createRefreshToken("refreshToken", userId, 30L * 24 * 60 * 60 * 1000L); // 30일 - // 2. 1)의 사용자 정보를 담아, accessToken과 refreshToken 발행 - String accessToken = jwtUtil.createAccessToken("accessToken", userId, role, 30 * 60 * 1000L); // 유효기간 30분 - String refreshToken = jwtUtil.createRefreshToken("refreshToken", userId, - 30 * 24 * 60 * 60 * 1000L); // 유효기간 30일 - - // 3. refreshToken을 DB에 저장 + // 1. refreshToken을 DB에 저장 Token refreshTokenEntity = Token.toEntity(user, refreshToken, LocalDateTime.now().plusDays(30)); tokenRepository.save(refreshTokenEntity); - // 4. JSON 응답으로, accessToken과 refreshToken 을 반환해준다. - response.setContentType("application/json"); - response.setCharacterEncoding("utf-8"); - - ObjectMapper objectMapper = new ObjectMapper(); // 객체 -> json 문자열로 변환 - String body = objectMapper.writeValueAsString( - Map.of( - "accessToken", accessToken, - "refreshToken", refreshToken - ) - ); - response.getWriter().write(body); + // 2. refreshToken을 HttpOnly 쿠키로 설정 + Cookie refreshTokenCookie = new Cookie("refreshToken", refreshToken); + refreshTokenCookie.setHttpOnly(true); // JS 접근 불가 + refreshTokenCookie.setSecure(false); // 운영환경 https라면 true + refreshTokenCookie.setPath("/"); + refreshTokenCookie.setMaxAge(30 * 24 * 60 * 60); // 30일 + response.addCookie(refreshTokenCookie); + + // 3. 프론트엔드로 리다이렉트 (accessToken만 쿼리로 전달) + String targetUrl = "http://localhost:5173/login/success?accessToken=" + accessToken; + response.sendRedirect(targetUrl); } } From e220e67fbe3ccdb13b283c4f9318a7cfba440831 Mon Sep 17 00:00:00 2001 From: jeonghyemin Date: Mon, 23 Jun 2025 16:47:31 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor(user):=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC=20=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/oauth2/OAuth2LoginSuccessHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java b/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java index fc9aeae..b9e14e9 100644 --- a/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java +++ b/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java @@ -56,9 +56,13 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo // 2. refreshToken을 HttpOnly 쿠키로 설정 Cookie refreshTokenCookie = new Cookie("refreshToken", refreshToken); refreshTokenCookie.setHttpOnly(true); // JS 접근 불가 - refreshTokenCookie.setSecure(false); // 운영환경 https라면 true + refreshTokenCookie.setSecure(false); // 운영환경 https라면 true로 변경 필요 refreshTokenCookie.setPath("/"); refreshTokenCookie.setMaxAge(30 * 24 * 60 * 60); // 30일 + // CSRF 방지를 위한 SameSite 속성 추가 (Servlet 3.1+ 지원시) + response.setHeader("Set-Cookie", + String.format("%s=%s; HttpOnly; Secure; Path=/; Max-Age=%d; SameSite=Lax", + "refreshToken", refreshToken, 30 * 24 * 60 * 60)); response.addCookie(refreshTokenCookie); // 3. 프론트엔드로 리다이렉트 (accessToken만 쿼리로 전달) From 0e5204cbc0260958a94702a34bcd0c618fabf76b Mon Sep 17 00:00:00 2001 From: jeonghyemin Date: Mon, 23 Jun 2025 16:52:56 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor(user):=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC=20=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/oauth2/OAuth2LoginSuccessHandler.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java b/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java index b9e14e9..fdbe236 100644 --- a/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java +++ b/src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java @@ -59,11 +59,9 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo refreshTokenCookie.setSecure(false); // 운영환경 https라면 true로 변경 필요 refreshTokenCookie.setPath("/"); refreshTokenCookie.setMaxAge(30 * 24 * 60 * 60); // 30일 - // CSRF 방지를 위한 SameSite 속성 추가 (Servlet 3.1+ 지원시) - response.setHeader("Set-Cookie", - String.format("%s=%s; HttpOnly; Secure; Path=/; Max-Age=%d; SameSite=Lax", - "refreshToken", refreshToken, 30 * 24 * 60 * 60)); response.addCookie(refreshTokenCookie); + response.addHeader("Set-Cookie", response.getHeader("Set-Cookie") + "; SameSite=Lax"); + // 3. 프론트엔드로 리다이렉트 (accessToken만 쿼리로 전달) String targetUrl = "http://localhost:5173/login/success?accessToken=" + accessToken;