Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,7 +29,6 @@
// 1. JWT 토큰 발급
// - 이때, JWT payload는 보안상 최소한의 정보(userId, role)만 담겠다
// 2. refreshToken만 DB에 저장
// 3. JSON 응답으로, accessToken과 refreshToken 을 반환해준다.
@Component
@RequiredArgsConstructor
@Slf4j
Expand All @@ -40,42 +40,32 @@ 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<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> 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");
// 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);
response.addHeader("Set-Cookie", response.getHeader("Set-Cookie") + "; SameSite=Lax");


ObjectMapper objectMapper = new ObjectMapper(); // 객체 -> json 문자열로 변환
String body = objectMapper.writeValueAsString(
Map.of(
"accessToken", accessToken,
"refreshToken", refreshToken
)
);
response.getWriter().write(body);
// 3. 프론트엔드로 리다이렉트 (accessToken만 쿼리로 전달)
String targetUrl = "http://localhost:5173/login/success?accessToken=" + accessToken;
response.sendRedirect(targetUrl);
Comment on lines +66 to +68
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

리다이렉트 URL 하드코딩 문제 해결 필요

현재 프론트엔드 URL이 하드코딩되어 있어서 개발/스테이징/운영 환경별로 다른 URL을 사용하기 어려울 것 같습니다. 또한 쿼리 파라미터로 access token을 전달하는 것도 보안상 고려사항이 있습니다.

다음과 같은 개선을 권장합니다:

-// 3. 프론트엔드로 리다이렉트 (accessToken만 쿼리로 전달)
-String targetUrl = "http://localhost:5173/login/success?accessToken=" + accessToken;
-response.sendRedirect(targetUrl);
+// 3. 프론트엔드로 리다이렉트 (환경별 URL 설정)
+String frontendUrl = getFrontendUrl(); // 환경별 설정에서 가져오기
+String targetUrl = frontendUrl + "/login/success";
+
+// Access token도 쿠키로 전달 (보안 강화)
+Cookie accessTokenCookie = new Cookie("accessToken", accessToken);
+accessTokenCookie.setHttpOnly(false); // JS에서 접근 가능해야 함
+accessTokenCookie.setSecure(true);
+accessTokenCookie.setPath("/");
+accessTokenCookie.setMaxAge(30 * 60); // 30분
+response.addCookie(accessTokenCookie);
+
+response.sendRedirect(targetUrl);

환경별 URL 설정을 위해 application.yml에 설정값 추가도 고려해보세요:

app:
  frontend:
    url: ${FRONTEND_URL:http://localhost:5173}
🤖 Prompt for AI Agents
In
src/main/java/com/example/gtable/global/security/oauth2/OAuth2LoginSuccessHandler.java
around lines 64 to 66, the redirect URL is hardcoded, which limits flexibility
across environments and poses security concerns by passing the access token as a
query parameter. To fix this, externalize the frontend URL by adding a
configuration property in application.yml (e.g., app.frontend.url) and inject
this property into the handler class. Then, construct the redirect URL
dynamically using the injected frontend URL. Additionally, consider a more
secure method to transmit the access token instead of including it in the query
string.

}

}