Skip to content

Conversation

@minjee2758
Copy link
Collaborator

@minjee2758 minjee2758 commented Jun 13, 2025

작업 내용

  • Spring Security에서 API 인증 실패 시 HTML 응답 대신 JSON 응답 반환하도록 수정함
  • api 호출 시 Google OAuth 리다이렉트 방지 및 JSON 에러 응답 구현함
  • JWT 필터 화이트리스트 기능 추가하여 특정 경로 인증 제외 처리함

변경 사항

  • SecurityConfig.exceptionHandling() 설정 추가하여 커스텀 예외 처리 핸들러 등록함
  • customAuthenticationEntryPoint() 빈 추가하여 인증 실패 시 JSON 응답 반환함
  • customAccessDeniedHandler() 빈 추가하여 권한 부족 시 JSON 응답 반환함
  • JwtFilter.shouldNotFilter() 메서드 추가하여 화이트리스트 경로 필터링 제외 처리함
  • 화이트리스트 경로: /auth/, /login/, /oauth2/** 등 인증 불필요 경로 설정함

트러블 슈팅

1. JwtFilter
문제: 인증이 필요없는 경로에서도 JWT 필터가 동작하여 인증 검사 수행함
-> 화이트리스트 경로는 JWT 검증 생략함

2. HTML 반환 오류
문제: OAuth2 설정으로 인해 인증되지 않은 요청이 자동으로 Google 로그인으로 리다이렉트됨
-> AuthenticationEntryPoint와 AccessDeniedHandler 설정으로 JSON 응답 우선 처리함


해결해야 할 문제

  • 화이트리스트 경로가 SecurityConfig와 JwtFilter에 중복 정의됨 => 설정 통합 고려 필요함

참고 사항

  • 에러 잘 뜹니당 이제ㅠㅠ
image

코드 리뷰 전 확인 체크리스트

  • 불필요한 콘솔 로그, 주석 제거
  • 커밋 메시지 컨벤션 준수 (type : )
  • 기능 정상 동작 확인

Summary by CodeRabbit

  • 신규 기능
    • 인증 및 인가 실패 시 JSON 형식의 에러 응답을 제공합니다.
  • 버그 수정
    • 인증 및 토큰 관련 엔드포인트에서 Authorization 헤더가 누락되었거나 잘못된 경우 명확한 에러 메시지를 반환합니다.
  • 리팩터
    • 토큰 처리 방식이 개선되어 서비스 레이어에서 직접 토큰 문자열을 사용하도록 변경되었습니다.
    • JWT 인증 필터에 화이트리스트 경로가 추가되어 일부 엔드포인트에서 토큰 검증이 생략됩니다.
    • API 엔드포인트에 공통 경로 /api가 추가되어 URL 구조가 일관되게 변경되었습니다.

@minjee2758 minjee2758 self-assigned this Jun 13, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 13, 2025

Walkthrough

인증 및 토큰 관리 로직이 리팩토링되어, 서비스 계층의 logoutrefreshToken 메서드는 더 이상 HttpServletRequest를 직접 받지 않고 토큰 문자열을 직접 인자로 받도록 변경되었습니다. 또한, JWT 필터에 화이트리스트 경로가 추가되고, 인증/인가 실패 시 JSON 에러 응답을 반환하는 커스텀 핸들러가 도입되었습니다.

Changes

파일/그룹 변경 요약
.../AuthService.java logoutrefreshToken 메서드가 HttpServletRequest 대신 토큰 문자열을 직접 인자로 받도록 시그니처 변경 및 내부 로직 수정, 로깅 추가
.../AuthController.java 컨트롤러에서 "Authorization" 헤더에서 토큰을 추출하여 서비스에 전달하도록 구현, 예외 처리 및 로깅 추가
.../JwtFilter.java JWT 필터에 인증 토큰 검증을 건너뛰는 화이트리스트 경로 추가, 관련 메서드 도입
.../SecurityConfig.java 인증/인가 실패 시 JSON 에러 응답을 반환하는 커스텀 핸들러 추가 및 등록, OAuth2 로그인 페이지 지정, 불필요한 필터 등록 제거
.../UserController.java 클래스 레벨에 /api 기본 요청 경로 매핑 추가

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AuthController
    participant AuthService
    participant Redis
    participant JwtFilter

    Client->>AuthController: /auth/logout (Authorization 헤더 포함)
    AuthController->>AuthService: logout(userId, token)
    AuthService->>Redis: 토큰 블랙리스트 등록 및 리프레시 토큰 삭제
    AuthService-->>AuthController: LogoutResponse
    AuthController-->>Client: 응답

    Client->>AuthController: /auth/refresh (Authorization 헤더 포함)
    AuthController->>AuthService: refreshToken(token)
    AuthService->>Redis: 리프레시 토큰 조회 및 검증
    AuthService-->>AuthController: RefreshTokenResponse
    AuthController-->>Client: 응답
Loading

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • pokerbearkr
  • Kimminu7

Poem

🐇
토큰을 쏙 빼서 서비스에 전해,
필터는 화이트리스트로 살짝 피해가네.
인증 실패엔 JSON으로 친절히 답하고,
로그 남기며 흐름도 또렷하게.
코드밭에 바람 불어 새로워진 오늘,
토끼는 기쁘게 깡총 뛰어 노래하네!


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fafd052 and 1b4e568.

📒 Files selected for processing (2)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/AuthController.java (3 hunks)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/AuthController.java
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🔭 Outside diff range comments (1)
src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java (1)

118-136: ⚠️ Potential issue

Redis 실패를 삼켜 버리면 운영 문제를 조기에 인지하기 어렵습니다
catch (Exception e) 에서 로그만 남기고 성공을 리턴하면 실제로 토큰이 블랙리스트에 등록되지 않아 보안 구멍이 발생할 수 있습니다.

  • 재시도 로직 또는
  • 실패 시에도 사용자에게 “일시적 오류” 를 알리고 재로그인 요구
    를 고려해 주세요.
🧹 Nitpick comments (5)
src/main/java/org/ezcode/codetest/common/security/util/JwtFilter.java (1)

108-110: 성능은 충분하지만, WHITE_LIST 가 작아도 매 요청 Arrays.stream() 사용은 불필요
정적 Set 으로 변환 후 contains/stream().anyMatch 비교에서 O(n)O(1) 로 단순화할 수 있습니다.

src/main/java/org/ezcode/codetest/presentation/usermanagement/AuthController.java (2)

55-58: replace("Bearer ", "") 는 토큰 내부 문자열까지 제거할 수 있습니다
토큰 값에 Bearer 가 포함될 경우(드물지만 가능) 전부 치환됩니다.

- .map(h -> h.replace("Bearer ", ""))
+ .map(h -> h.substring(7)) // "Bearer ".length == 7

66-72: 중복 코드 추출 제안
토큰 헤더 파싱 로직이 logout·refresh 두 곳에 동일하게 존재합니다. private String extractToken(HttpServletRequest) 헬퍼 메서드로 분리하면 중복을 제거하고 테스트하기도 수월합니다.

src/main/java/org/ezcode/codetest/common/security/config/SecurityConfig.java (2)

94-116: 에러 메시지 하드코딩은 예외 케이스를 과도하게 단순화합니다
"토큰이 필요합니다" 로 고정하면 만료·서명 오류·지원되지 않는 토큰 등 구분이 불가합니다.
authException.getMessage() 활용 또는 커스텀 예외 코드/메시지를 포함시켜 클라이언트가 원인을 식별할 수 있도록 개선이 필요합니다.


118-140: AccessDeniedHandler 도 동일하게 세분화 필요
현재는 모든 403을 "접근 권한이 없습니다" 로 반환합니다. 세밀한 권한 정책(등급 제한 등)이 있다면 코드와 메시지를 함께 내려주세요.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3bbae9e and fafd052.

📒 Files selected for processing (4)
  • src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java (3 hunks)
  • src/main/java/org/ezcode/codetest/common/security/config/SecurityConfig.java (3 hunks)
  • src/main/java/org/ezcode/codetest/common/security/util/JwtFilter.java (3 hunks)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/AuthController.java (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (1)
src/main/java/org/ezcode/codetest/common/security/util/JwtFilter.java (1)

49-56: startsWith 매칭은 의도치 않은 우회 가능성을 열 수 있습니다
예) /auth/signupExtra 요청도 /auth/signup 으로 시작하므로 필터를 우회합니다. 필요하다면 AntPathMatcher"/auth/signup" 정확 매칭 또는 "/auth/signup/**" 패턴 매칭을 명시적으로 지정하세요.

Comment on lines +35 to +43
// 토큰 검증을 건너뛸 경로들
private static final String[] WHITE_LIST = {
"/auth/signin",
"/auth/signup",
"/auth/refresh",
"/swagger-ui",
"/v3/api-docs",
};

Copy link

@coderabbitai coderabbitai bot Jun 13, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

화이트리스트 하드코딩은 유지‧보수 비용을 높입니다
SecurityConfig 에서도 동일(혹은 더 광범위한) 경로를 permitAll() 로 선언하고 있어 두 곳이 불필요하게 중복됩니다. 경로가 추가/변경될 때 한쪽만 수정되면 필터와 인가 규칙이 불일치할 위험이 있습니다.

  • 공통 상수 클래스로 분리하거나
  • application.yml 의 리스트를 주입받아 사용하도록 리팩터링하면 추후 운영 중 경로가 늘어나더라도 한 군데만 수정하면 됩니다.
🤖 Prompt for AI Agents
In src/main/java/org/ezcode/codetest/common/security/util/JwtFilter.java around
lines 35 to 43, the whitelist paths are hardcoded, causing duplication with
SecurityConfig and increasing maintenance risk. Refactor by extracting these
paths into a shared constant class or configure them in application.yml and
inject the list into JwtFilter. This centralizes path management, ensuring
consistency and easier updates.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

고도화할떄 별도 클래스로 분리할게

Copy link

Choose a reason for hiding this comment

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

@minjee2758 네, 알겠습니다. 차후 고도화 단계에서 공통 클래스로 분리하시려는 계획 확인했습니다.
추적 용으로 별도 이슈를 만들어 두면 놓치지 않고 진행하실 수 있는데, 이슈 생성을 도와드릴까요? 필요 없으시면 그대로 두셔도 됩니다.

Comment on lines 52 to 60
@AuthenticationPrincipal AuthUser authUser,
HttpServletRequest request) {
return ResponseEntity.status(HttpStatus.OK).body(authService.logout(authUser.getId(), request));

String token = Optional.ofNullable(request.getHeader("Authorization"))
.map(h -> h.replace("Bearer ", ""))
.orElseThrow(()-> new AuthException(AuthExceptionCode.INVALID_AUTHORIZATION_HEADER));

return ResponseEntity.status(HttpStatus.OK).body(authService.logout(authUser.getId(), token));
}
Copy link

@coderabbitai coderabbitai bot Jun 13, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

@AuthenticationPrincipal 가 null 일 수 있어 NPE 위험
/auth/**permitAll() 로 열려 있으므로 토큰 없이 호출되면 authUser 가 주입되지 않습니다.

Objects.requireNonNull(authUser, "로그아웃은 인증이 필요합니다");

또는 SecurityConfig 에서 /auth/logout 을 인증 필요 경로로 이동하세요.

🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/presentation/usermanagement/AuthController.java
around lines 52 to 60, the parameter annotated with @AuthenticationPrincipal
(authUser) can be null because the /auth/** endpoints are configured with
permitAll(), allowing calls without a token. To fix the potential
NullPointerException, add a null check for authUser at the start of the method
using Objects.requireNonNull(authUser, "로그아웃은 인증이 필요합니다"); or alternatively,
update the SecurityConfig to require authentication for the /auth/logout
endpoint so that authUser is always injected.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

테스트하면서 편하려고 일단 풀어놨어

Copy link

Choose a reason for hiding this comment

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

@minjee2758 네, 확인했습니다! 테스트용으로 잠시 개방해 두신 것이라면 이해됩니다. 다만

  • 토큰 없이 /auth/logout 을 호출할 때 authUsernull 이면 NPE가 발생해 500 응답이 내려갈 수 있으니, 테스트 중에도 간단한 널 체크로 401/403 정도를 내려주면 디버깅이 수월할 거예요.
if (authUser == null) {
    throw new AuthException(AuthExceptionCode.UNAUTHORIZED); // 혹은 커스텀 메시지
}
  • 나중에 배포 단계에서 잊지 않고 다시 인증 필요 경로로 옮길 수 있도록 // TODO: release 시 permitAll 해제 같은 주석을 남겨 두시는 것도 권장드립니다.

추가로, 프로덕션 전환 시점에 이 항목을 추적할 이슈가 필요하시면 말씀해 주세요. 🤖

Comment on lines +56 to +58
.exceptionHandling(except -> except
.authenticationEntryPoint(customAuthenticationEntryPoint())
.accessDeniedHandler(customAccessDeniedHandler()))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

/auth/** 전체를 permitAll() 로 두면 보호가 필요한 API(예: 로그아웃)가 열려 있습니다
JWT 필터로 인증 객체가 주입되지 않더라도 컨트롤러 접근이 허용됩니다. /auth/logout 만큼은 authenticated() 로 분리하거나 메서드에 @PreAuthorize 를 추가해 무조건 인증을 보장하세요.

🤖 Prompt for AI Agents
In src/main/java/org/ezcode/codetest/common/security/config/SecurityConfig.java
around lines 56 to 58, the current configuration permits all requests to
/auth/**, which unintentionally exposes protected APIs like /auth/logout. To fix
this, modify the security rules to exclude /auth/logout from permitAll() by
requiring authentication for it, either by configuring it with authenticated()
in the security filter chain or by adding @PreAuthorize annotations on the
logout controller method to enforce authentication.

Comment on lines +140 to 150
public RefreshTokenResponse refreshToken(String token) {
log.info("서비스 입장");

if (bearToken == null || !bearToken.startsWith("Bearer ")) {
throw new AuthException(AuthExceptionCode.INVALID_AUTHORIZATION_HEADER);
}
String refreshToken = jwtUtil.substringToken(bearToken);

if (!jwtUtil.validateToken(refreshToken)){
throw new AuthException(AuthExceptionCode.INVALID_REFRESH_TOKEN);
}

Long userId = jwtUtil.getUserId(refreshToken);
Long userId = jwtUtil.getUserId(token);

log.info("유저 아이디 가져옴");
String savedToken = redisTemplate.opsForValue().get("RefreshToken:" + userId);

if (savedToken==null || !savedToken.equals(refreshToken)){
log.info("저장된 토큰 가져옴");
if (savedToken==null || !savedToken.equals(token)){
log.error("저장된 토큰 없음");
throw new AuthException(AuthExceptionCode.INVALID_REFRESH_TOKEN);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refresh 토큰 자체 유효성 검증이 부족합니다
Redis 비교 전에 jwtUtil.validateToken(token) 과 만료 여부를 검사하지 않으면 만료된 RefreshToken 으로도 새 AccessToken 이 발급될 수 있습니다.

🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java
around lines 140 to 150, the refreshToken method lacks validation of the refresh
token's validity and expiration before comparing it with the stored token in
Redis. To fix this, add a call to jwtUtil.validateToken(token) before fetching
the saved token from Redis, and if the token is invalid or expired, throw an
AuthException with the appropriate error code to prevent issuing a new access
token with an expired refresh token.

@NCookies NCookies merged commit debafb9 into dev Jun 13, 2025
2 checks passed
@NCookies NCookies deleted the refactor/user-security branch June 13, 2025 05:12
@coderabbitai coderabbitai bot mentioned this pull request Jun 14, 2025
3 tasks
thezz9 pushed a commit that referenced this pull request Jun 14, 2025
* refactor : logout HttpServeletRequest를 컨트롤러에서 String으로 추출 후 service로 전달

* refactor : whitelist 등록

* refactor : 에러 발생 시, html응답 대신 Json응답으로 받을 수 있도록 리팩토링

* refactor : requestMapping으로 앞에 일괄적으로 /api 달기
thezz9 added a commit that referenced this pull request Jun 14, 2025
* build : update build.gradle for Redis

* refactor : Judge0 비동기 병렬 요청 구현

* refactor : 비동기 병렬 요청 구현을 위한 Application, dto

* refactor : 비동기 병렬 요청 구현을 위한 config 파일

* feat : Redis Stream 구현

* refactor : 병렬 요청에서 SSE 구별을 위한 인메모리 저장

* refactor : 테스트용 UI

* refactor : 그룹핑 처리 DTO로 책임 분리

* refactor : Judge0 polling 예외처리 고도화

* refactor : api 경로 수정

* refactor : 서비스 로직 리팩토링

* refactor : 모델 객체 추가

* refactor : 예외 코드 추가

* Refactor : html(google oauth)로 리다이렉팅되는 문제 해결 (#57)

* refactor : logout HttpServeletRequest를 컨트롤러에서 String으로 추출 후 service로 전달

* refactor : whitelist 등록

* refactor : 에러 발생 시, html응답 대신 Json응답으로 받을 수 있도록 리팩토링

* refactor : requestMapping으로 앞에 일괄적으로 /api 달기

* feat : 게임 캐릭터 생성 기능, 아이템 생성 기능, 아이템 뽑기, 스테이터스 확인, 아이템 장착 기능 추가 (#58)

* feat : 게임 도메인 엔티티 생성 및 스킬, 아이템 효과 정의

* feat : 게임 도메인 서비스, 아이템 뽑기 도메인서비스 추가, 랜덤 인카운터 추가

* feat : 아이템 장착, 뽑기, 인벤토리 오픈, 스탯 확인, 캐릭터 생성 기능 추가

# Conflicts:
#	src/main/java/org/ezcode/codetest/common/security/config/SecurityConfig.java

* chore : properties 수정

* chore : properties 수정

* chore : securityconfig 오타수정

* chore : 오타수정

* chore : 오타수정

* chore : 무수히 많은 오타수정, 누락된 어노테이션 추가

* docs : 환경변수 추가

* docs : 오타수정

---------

Co-authored-by: pokerbearkr <[email protected]>

* Refactor : Filter WhiteList 수정 (#59)

* refactor : api 경로 추가, oauth 경로 수정

* refactor : whitelist 경로 수정

* refactor : redirect url 수정

* refactor : Submission Stream

---------

Co-authored-by: MIN <[email protected]>
Co-authored-by: chat26666 <[email protected]>
Co-authored-by: pokerbearkr <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants