[Feat]#7 authentication - #8
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughSpring Cloud Gateway에 대한 JWT 기반 인증과 CORS 구성이 추가되었습니다. Spring Security, JWT 디코더 및 권한 변환기, 글로벌 인증 필터가 도입되고 애플리케이션 환경 설정과 빌드 의존성이 업데이트되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Gateway as Spring Cloud Gateway
participant SecurityChain as Security Filter Chain
participant RoleConv as GatewayRoleConverter
participant JwtFilter as JwtAuthenticationFilter
participant Service as Downstream Service
Client->>Gateway: HTTP Request (with/without Authorization)
Gateway->>SecurityChain: Apply SecurityWebFilterChain
SecurityChain->>SecurityChain: Decode & Validate JWT (ReactiveJwtDecoder)
SecurityChain->>RoleConv: Convert JWT -> Authorities
RoleConv-->>SecurityChain: GrantedAuthority
SecurityChain-->>JwtFilter: Authentication present (JwtAuthenticationToken)
JwtFilter->>JwtFilter: Extract subject and role claim
JwtFilter->>Gateway: Mutate Request Headers (X-User-Id, X-User-Role)
Gateway->>Service: Forward Mutated Request
Service-->>Client: Response
sequenceDiagram
actor User
participant ClientApp as Client App
participant Gateway as API Gateway
participant AuthSvc as Auth Service
participant JwtDecoder as JWT Generator/Decoder
User->>ClientApp: Submit credentials
ClientApp->>Gateway: POST /api/v1/auth/login
Gateway->>AuthSvc: Route to Auth Service
AuthSvc->>JwtDecoder: Generate signed JWT (role claim)
JwtDecoder-->>AuthSvc: JWT Token
AuthSvc-->>ClientApp: Return JWT
ClientApp->>Gateway: Subsequent requests with Authorization
Gateway->>JwtDecoder: Verify / Decode JWT
JwtDecoder-->>Gateway: Claims (sub, role)
Gateway->>ClientApp: Authenticated response / proxied request
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Review rate limit: 2/3 reviews remaining, refill in 20 minutes. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/main/java/com/michelet/gateway/infrastructure/config/SecurityConfig.java (1)
42-47:jwt.secret에 대한 길이/유효성 검증을 빈 생성 시점에 추가해 주세요.HS256 키가 너무 짧거나 비어 있으면 운영 중 토큰 검증 실패/보안 약화로 이어질 수 있어, 애플리케이션 시작 시 fail-fast 하는 편이 안전합니다.
🔧 제안 diff
`@Bean` public ReactiveJwtDecoder reactiveJwtDecoder(`@Value`("${jwt.secret}") String secret){ + byte[] secretBytes = secret == null ? new byte[0] : secret.getBytes(StandardCharsets.UTF_8); + if (secretBytes.length < 32) { + throw new IllegalStateException("jwt.secret must be at least 32 bytes for HS256"); + } SecretKey secretKey = new SecretKeySpec( - secret.getBytes(StandardCharsets.UTF_8), + secretBytes, "HmacSHA256" ); return NimbusReactiveJwtDecoder.withSecretKey(secretKey).build(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/com/michelet/gateway/infrastructure/config/SecurityConfig.java` around lines 42 - 47, Check and validate the jwt.secret value inside the reactiveJwtDecoder bean factory: ensure it's non-empty and meets a minimum entropy/length (e.g., at least 32 characters/bytes) before constructing the SecretKey (SecretKey secretKey = new SecretKeySpec(...)); if validation fails, throw an unchecked exception (e.g., IllegalArgumentException) so the application fails fast during startup and does not build the NimbusReactiveJwtDecoder.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/main/java/com/michelet/gateway/infrastructure/security/JwtAuthenticationFilter.java`:
- Around line 39-43: The request mutation in JwtAuthenticationFilter currently
uses normalize() which can turn null into an empty header and does not
explicitly remove existing headers; update the ServerHttpRequest mutation (the
exchange.getRequest().mutate() call that builds mutatedRequest) to first remove
any existing USER_ID_HEADER and USER_ROLE_HEADER values and then set each header
only when the JWT-derived value is non-null/non-empty (avoid normalize()
producing empty strings); reference the USER_ID_HEADER/USER_ROLE_HEADER
constants, the normalize(String) helper, and the mutatedRequest creation to
locate and change the logic so headers are explicitly removed and only added
when valid values exist.
- Around line 22-27: 현재 filter 메서드의 onErrorResume이 에러 발생 시 원본 요청으로
chain.filter(exchange)를 다시 호출해 비멱등 요청을 중복 처리하게 하므로, onErrorResume에서
chain.filter(exchange)를 재호출하지 말고 에러를 그대로 전파하거나 적절한 에러 응답을 반환하도록 변경하세요; 구체적으로
JwtAuthenticationFilter.filter 내부의 onErrorResume 대신 에러를 그대로 반환하는 Mono.error(ex)를
사용하거나 ServerWebExchange에 적절한 에러 상태(예: 4xx/5xx) 응답을 쓰고 Mono.empty/Mono<Void>로
마무리하여 addHeaders/chain.filter 재실행을 방지하세요.
In `@src/main/resources/application-docker.yml`:
- Line 4: defaultZone 설정이 환경변수가 없을 때 리터럴로 남아 Eureka 연결에 실패하므로
application-docker.yml의 defaultZone에 EUREKA_CONTAINER_NAME 및 EUREKA_PORT에 대한 안전한
기본값을 추가하거나 환경 변수 확장 문법을 사용해 기본값을 지정하세요; 예를 들어 defaultZone 항목에서 참조되는
EUREKA_CONTAINER_NAME, EUREKA_PORT 식별자를 찾아(application-local.yml의 설정을 참고) 안전한 기본
호스트명/포트로 대체하거나 ${VAR:default} 형식으로 기본값을 제공하도록 변경하면 됩니다.
In `@src/main/resources/application.yml`:
- Around line 65-69: 기본 프로파일과 각 환경 프로파일(application-local.yml,
application-docker.yml)에 jwt.secret이 빠져 있어 SecurityConfig의
`@Value`("${jwt.secret}") 주입 시 애플리케이션이 실패하므로, jwt.secret 값을 각 프로파일에 명시하거나 환경변수로
주입되도록 설정하세요(예: SecurityConfig가 기대하는 환경변수 이름 사용 또는 프로퍼티 기본값 제공). 또한
cors.allowed-origins가 비어 있어 CORS가 차단되니 기본 application.yml과 docker/local 프로파일에
cors.allowed-origins에 허용할 원본 목록을 명시하거나 환경변수로 주입되게 설정하여 SecurityConfig와 CORS 구성이
모두 정상 동작하도록 하세요.
---
Nitpick comments:
In
`@src/main/java/com/michelet/gateway/infrastructure/config/SecurityConfig.java`:
- Around line 42-47: Check and validate the jwt.secret value inside the
reactiveJwtDecoder bean factory: ensure it's non-empty and meets a minimum
entropy/length (e.g., at least 32 characters/bytes) before constructing the
SecretKey (SecretKey secretKey = new SecretKeySpec(...)); if validation fails,
throw an unchecked exception (e.g., IllegalArgumentException) so the application
fails fast during startup and does not build the NimbusReactiveJwtDecoder.
🪄 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: b971c1e2-55f4-464c-842e-f8229eed0d27
📒 Files selected for processing (11)
.gitignorebuild.gradlesrc/main/java/com/michelet/gateway/infrastructure/config/CorsConfig.javasrc/main/java/com/michelet/gateway/infrastructure/config/CorsProperties.javasrc/main/java/com/michelet/gateway/infrastructure/config/SecurityConfig.javasrc/main/java/com/michelet/gateway/infrastructure/security/GatewayRoleConverter.javasrc/main/java/com/michelet/gateway/infrastructure/security/JwtAuthenticationFilter.javasrc/main/resources/application-docker.ymlsrc/main/resources/application-local.ymlsrc/main/resources/application-test.ymlsrc/main/resources/application.yml
📝 작업 내용
🚀 주요 변경 사항
✅ 자체 체크리스트 (필수)
./gradlew build실행 결과 정상 (인증샷 첨부)📸 테스트 인증샷
💬 리뷰어 전달사항 (선택)
📎 참고 자료
Summary by CodeRabbit
릴리스 노트
New Features
Improvements