Skip to content

feat: WS AT 만료 시 능동 종료 처리#112

Merged
parkjuyeong0312 merged 11 commits into
mainfrom
feature/ws-at-expiry-enforcement
May 29, 2026
Merged

feat: WS AT 만료 시 능동 종료 처리#112
parkjuyeong0312 merged 11 commits into
mainfrom
feature/ws-at-expiry-enforcement

Conversation

@parkjuyeong0312
Copy link
Copy Markdown
Member

@parkjuyeong0312 parkjuyeong0312 commented May 29, 2026

변경 내용

  • WebSocket handshake에서 access token claims(userId, expiresAt)를 함께 저장하도록 변경
  • STOMP CONNECT 및 SEND/SUBSCRIBE 시점에 tokenExpiresAt을 검사하고, 만료 시 세션을 능동 close하도록 추가
  • WebSocketSessionRegistry/decorator와 관련 단위 테스트, WS AT 만료 설계/실행 문서 추가

변경 이유

  • handshake 이후 access token이 만료된 WebSocket 세션에서 후속 frame이 broker/handler로 전달되지 않도록 차단하기 위해
  • 클라이언트의 애플리케이션 레벨 PING 기반 재연결 흐름과 서버 만료 검사를 연결하기 위해

테스트

  • ./gradlew build
  • /review-code-against-docs 스킬로 검증
  • 그 외 수동 검증: 2026-05-29 /app/ping 기반 access token refresh/reconnect 흐름 확인 기록

체크리스트

  • PR 제목이 커밋 컨벤션 형식을 따른다.
  • 변경 사유를 PR 설명에 기록했다.
  • 테스트 방법과 결과를 기록했다.
  • 문서 변경이 필요한 경우 반영했다.

하네스 변경 체크리스트

  • CLAUDE.md(AGENTS.md) 변경이 포함되어 있는가? 해당 없음
  • 변경 사유가 PR 설명에 기록되어 있는가?
  • 기존 규칙과 충돌하지 않는가?
  • 팀원에게 변경 사항을 공유했는가?

Summary by CodeRabbit

릴리스 노트

  • New Features

    • WebSocket 세션에서 토큰 만료 시 서버가 능동적으로 세션을 종료하여 만료된 연결 상태 방지
    • 토큰 만료 후 재연결 프로세스 개선으로 무한 재연결 루프 제거
  • Documentation

    • WebSocket 토큰 만료 대응 설계 문서 추가
    • 토큰 갱신 및 재연결 절차 설계 문서 추가
    • 구현 계획 및 검증 방법 문서 추가

Review Change Stack

Copilot AI review requested due to automatic review settings May 29, 2026 14:26
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@parkjuyeong0312, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 46 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ce6c241-030b-4e25-8e72-4842744df6fd

📥 Commits

Reviewing files that changed from the base of the PR and between 7dd2f8b and 7008086.

📒 Files selected for processing (2)
  • src/main/java/com/howaboutus/backend/realtime/config/StompAuthenticationInterceptor.java
  • src/main/java/com/howaboutus/backend/realtime/config/TokenExpiryChannelInterceptor.java
📝 Walkthrough

Walkthrough

WebSocket AT 만료 시 서버가 세션을 능동 종료하는 기능을 구현합니다. JWT 토큰 클레임을 Handshake 시 세션 속성에 저장하고, STOMP CONNECT 및 SEND/SUBSCRIBE 프레임 처리 시 저장된 만료 시각을 검증하여 만료된 세션을 종료 후 재연결을 유도합니다.

Changes

WebSocket 토큰 만료 감시 및 종료

Layer / File(s) Summary
토큰 클레임 추출 계약 정의
src/main/java/com/howaboutus/backend/auth/service/dto/AuthTokenClaims.java, src/main/java/com/howaboutus/backend/auth/service/JwtProvider.java, src/test/java/com/howaboutus/backend/auth/service/JwtProviderTest.java
JWT 페이로드에서 userIdexpiresAt을 추출하는 AuthTokenClaims 레코드 타입과 JwtProvider.extractClaims() 메서드를 추가하고, 만료/무효 토큰 케이스를 예외로 처리하는 단위 테스트를 포함.
세션 속성 확장 및 검증
src/main/java/com/howaboutus/backend/realtime/config/WebSocketSessionAttributes.java
토큰 만료 시각을 저장하기 위한 TOKEN_EXPIRES_AT 상수와 requireExpiresAt() 검증 헬퍼 메서드를 추가.
Handshake에서 토큰 클레임 저장
src/main/java/com/howaboutus/backend/realtime/config/WebSocketHandshakeInterceptor.java, src/test/java/com/howaboutus/backend/realtime/config/WebSocketHandshakeInterceptorTest.java
beforeHandshake에서 토큰 클레임 전체를 세션 속성에 저장하도록 변경하고, 토큰 검증 실패 시 USER_IDTOKEN_EXPIRES_AT을 모두 제거하는 처리를 추가.
STOMP CONNECT에서 만료 검증
src/main/java/com/howaboutus/backend/realtime/config/StompAuthenticationInterceptor.java, src/test/java/com/howaboutus/backend/realtime/config/StompAuthenticationInterceptorTest.java
CONNECT 프레임 시 세션 속성의 TOKEN_EXPIRES_AT을 주입된 Clock 기준으로 검증하고, 만료되었거나 누락되면 INVALID_TOKEN 예외를 발생시키는 로직 추가.
세션 라이프사이클 등록 및 해제
src/main/java/com/howaboutus/backend/realtime/config/WebSocketSessionRegistry.java, src/main/java/com/howaboutus/backend/realtime/config/TokenExpiryWebSocketHandlerDecorator.java, src/test/java/com/howaboutus/backend/realtime/config/WebSocketSessionRegistryTest.java, src/test/java/com/howaboutus/backend/realtime/config/TokenExpiryWebSocketHandlerDecoratorTest.java
WebSocket 세션을 ID 기준으로 등록/해제/종료하는 WebSocketSessionRegistry를 추가하고, 연결 성립/종료 시 레지스트리에 자동으로 등록/해제하는 TokenExpiryWebSocketHandlerDecorator 데코레이터를 구현.
SEND/SUBSCRIBE에서 만료 감시 및 종료
src/main/java/com/howaboutus/backend/realtime/config/TokenExpiryChannelInterceptor.java, src/test/java/com/howaboutus/backend/realtime/config/TokenExpiryChannelInterceptorTest.java
STOMP 메시지의 SEND/SUBSCRIBE 프레임에서 TOKEN_EXPIRES_ATClock.now()와 비교하여 만료 여부를 검사하고, 만료 시 ACCESS_TOKEN_EXPIRED 상태로 세션 종료 후 INVALID_TOKEN 예외를 발생시킴.
WebSocket 설정 통합
src/main/java/com/howaboutus/backend/realtime/config/WebSocketConfig.java
새로운 TokenExpiryChannelInterceptor를 inbound channel 인터셉터 체인에 추가하고, TokenExpiryWebSocketHandlerDecorator를 transport 데코레이터 팩토리로 등록하며, 의존성 주입 구성 확장.
설계 및 구현 계획 문서
docs/superpowers/specs/2026-05-28-sockjs-token-refresh-design.md, docs/superpowers/specs/2026-05-29-ws-at-expiry-enforcement-design.md, docs/superpowers/plans/2026-05-29-ws-at-expiry-enforcement.md
WebSocket AT 만료 감시 기능의 전체 아키텍처, 컴포넌트 변경 사항, 구현 체크리스트, FE 재연결 흐름을 상세히 문서화하고 Task 1~9로 구현 단계를 명시.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • how-about-us/backend-server#102: WebSocket STOMP interceptor 및 설정 관련 PR로, 본 PR에서 같은 WebSocketConfig 클래스에 새로운 inbound channel 인터셉터와 transport 데코레이터를 추가합니다.

Suggested labels

backend, test

Suggested reviewers

  • minbros

Poem

🐰 토큰이 만료되면, 로그아웃이 되고,
세션은 조용히 종료되네.
PING이 날아올 때마다 검사하며,
사용자는 다시 로그인하는 춤을 춘다.
보안 수호자, 만료의 감시자여!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항(WebSocket AT 만료 시 능동 종료)을 명확하게 요약하고 있으며, 커밋 컨벤션 형식(feat:)을 따르고 있습니다.
Description check ✅ Passed PR 설명이 변경 내용, 변경 이유, 테스트 항목을 모두 포함하고 있으며, 체크리스트 항목도 완성되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/howaboutus/backend/realtime/config/WebSocketSessionAttributes.java`:
- Around line 28-37: The helper method requireExpiresAt in
WebSocketSessionAttributes appears unused; search the codebase (including tests
and any reflective access) for requireExpiresAt and direct session attribute
access to confirm whether it's dead; if truly unused delete the method,
otherwise refactor by extracting the expires-at validation into a shared utility
(e.g., SessionAttributeUtils.requireExpiresAt) and update all duplicates and
callers to use that single method (ensure visibility and package placement), and
add/adjust unit tests to cover the consolidated behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 447f9a60-2788-4be6-962f-e4b61b47f13f

📥 Commits

Reviewing files that changed from the base of the PR and between 33e3838 and 7dd2f8b.

📒 Files selected for processing (18)
  • docs/superpowers/plans/2026-05-29-ws-at-expiry-enforcement.md
  • docs/superpowers/specs/2026-05-28-sockjs-token-refresh-design.md
  • docs/superpowers/specs/2026-05-29-ws-at-expiry-enforcement-design.md
  • src/main/java/com/howaboutus/backend/auth/service/JwtProvider.java
  • src/main/java/com/howaboutus/backend/auth/service/dto/AuthTokenClaims.java
  • src/main/java/com/howaboutus/backend/realtime/config/StompAuthenticationInterceptor.java
  • src/main/java/com/howaboutus/backend/realtime/config/TokenExpiryChannelInterceptor.java
  • src/main/java/com/howaboutus/backend/realtime/config/TokenExpiryWebSocketHandlerDecorator.java
  • src/main/java/com/howaboutus/backend/realtime/config/WebSocketConfig.java
  • src/main/java/com/howaboutus/backend/realtime/config/WebSocketHandshakeInterceptor.java
  • src/main/java/com/howaboutus/backend/realtime/config/WebSocketSessionAttributes.java
  • src/main/java/com/howaboutus/backend/realtime/config/WebSocketSessionRegistry.java
  • src/test/java/com/howaboutus/backend/auth/service/JwtProviderTest.java
  • src/test/java/com/howaboutus/backend/realtime/config/StompAuthenticationInterceptorTest.java
  • src/test/java/com/howaboutus/backend/realtime/config/TokenExpiryChannelInterceptorTest.java
  • src/test/java/com/howaboutus/backend/realtime/config/TokenExpiryWebSocketHandlerDecoratorTest.java
  • src/test/java/com/howaboutus/backend/realtime/config/WebSocketHandshakeInterceptorTest.java
  • src/test/java/com/howaboutus/backend/realtime/config/WebSocketSessionRegistryTest.java

@sonarqubecloud
Copy link
Copy Markdown

@parkjuyeong0312 parkjuyeong0312 merged commit 3cd052f into main May 29, 2026
4 checks passed
@parkjuyeong0312 parkjuyeong0312 deleted the feature/ws-at-expiry-enforcement branch May 29, 2026 14:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants