Skip to content

Conversation

@GoGradually
Copy link
Collaborator

변경된 점

  • jwt 인증용 필터 추가
  • k3s용 deployment/service 추가
  • cors 설정 추가
  • ci/cd 기본 설정 적용

- cors 관리를 스프링 시큐리티에서 수행하도록 변경
Copilot AI review requested due to automatic review settings December 30, 2025 13:55
Copy link

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.

Pull request overview

This PR introduces JWT authentication to the gateway service, transforming it into a centralized authentication point for the microservices architecture. The implementation adds security filters, CORS configuration, and deployment manifests for Kubernetes (k3s) environments.

Key Changes:

  • JWT authentication filter with RSA public key verification
  • CORS configuration for cross-origin requests
  • Gateway filter to extract member ID from JWT and forward as custom header
  • Kubernetes deployment, service, and ingress configurations
  • CI/CD pipelines for automated testing and deployment

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/main/java/me/pinitgateway/security/SecurityConfig.java Defines security filter chains with JWT authentication and public endpoint exceptions
src/main/java/me/pinitgateway/security/JwtAuthenticationProvider.java Implements reactive authentication manager for JWT validation
src/main/java/me/pinitgateway/security/CorsProperties.java Configuration properties class for CORS settings
src/main/java/me/pinitgateway/jwt/JwtTokenProvider.java Handles JWT parsing, validation, and claims extraction using RSA public key
src/main/java/me/pinitgateway/jwt/JwtAuthenticationToken.java Custom authentication token for JWT-based authentication
src/main/java/me/pinitgateway/jwt/JwtAuthenticationFilter.java WebFlux filter to extract and authenticate JWT from Authorization header
src/main/java/me/pinitgateway/jwt/RsaKeyProvider.java Utility to load RSA public key from PEM file
src/main/java/me/pinitgateway/filter/JwtSubToMemberIdHeaderGatewayFilterFactory.java Gateway filter to forward member ID from JWT as X-Member-Id header to downstream services
src/main/resources/application-prod.yml Production configuration with gateway routes and JWT filter registration
src/main/resources/application-dev.yml Development configuration with local service routing
src/test/resources/application.yml Test configuration with CORS and JWT public key path
src/test/resources/jwt-public-key.pem RSA public key for test environment
k8s/deployment.yaml Kubernetes deployment manifest with rolling update strategy and health probes
k8s/service.yaml Kubernetes service definition for gateway
k8s/ingress.yaml Updates ingress to route all traffic through gateway service
.github/workflows/ci.yaml CI workflow for build and test on pull requests
.github/workflows/cd.yml CD workflow for build, push, and deploy to k3s cluster
build.gradle Adds JJWT dependencies for JWT handling

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +129 to +139
* 현재 필터 규칙
* - /actuator/health/liveness : 인증 안함
* - /actuator/health/readiness : 인증 안함
* - /v3/** : 인증 안함
* - /swagger-ui/** : 인증 안함
* - /async-api/** : 인증 안함
* - auth host + /login : 인증 안함
* - auth host + /signup : 인증 안함
* - auth host + /refresh : 인증 안함
* - auth host + /login/** : 인증 안함
* - 그 외 : 인증 필요
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The comment is in Korean while the code uses English. For consistency and international collaboration, code comments should be in English.

Suggested change
* 현재 필터 규칙
* - /actuator/health/liveness : 인증 안함
* - /actuator/health/readiness : 인증 안함
* - /v3/** : 인증 안함
* - /swagger-ui/** : 인증 안함
* - /async-api/** : 인증 안함
* - auth host + /login : 인증 안함
* - auth host + /signup : 인증 안함
* - auth host + /refresh : 인증 안함
* - auth host + /login/** : 인증 안함
* - : 인증 필요
* Current filter rules:
* - /actuator/health/liveness : no authentication
* - /actuator/health/readiness : no authentication
* - /v3/** : no authentication
* - /swagger-ui/** : no authentication
* - /async-api/** : no authentication
* - auth host + /login : no authentication
* - auth host + /signup : no authentication
* - auth host + /refresh : no authentication
* - auth host + /login/** : no authentication
* - all other requests : authentication required

Copilot uses AI. Check for mistakes.
defaultMode: 0444
containers:
- name: app
image: ghcr.io/pinit-scheduler/pinit-gateway/app:${GITHUB_SHA} # GITHUB_SHA 환경변수는 GitHub Actions에서 설정되며 envsubst로 치환됩니다.
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The comment is in Korean while the code uses English. For consistency, code comments should be in English.

Suggested change
image: ghcr.io/pinit-scheduler/pinit-gateway/app:${GITHUB_SHA} # GITHUB_SHA 환경변수는 GitHub Actions에서 설정되며 envsubst로 치환됩니다.
image: ghcr.io/pinit-scheduler/pinit-gateway/app:${GITHUB_SHA} # The GITHUB_SHA environment variable is set by GitHub Actions and substituted using envsubst.

Copilot uses AI. Check for mistakes.

private ServerWebExchange mutateHeader(ServerWebExchange exchange, String sub) {
var request = exchange.getRequest().mutate()
// 외부에서 X-Member-Id를 임의로 넣어오는 spoofing 방지
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The comment is in Korean while code and other comments use English. For consistency, code comments should be in English.

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +29
} catch (Exception e) {
return false;
}
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The token validation catches all exceptions and returns false, hiding potentially important error information. While this prevents information leakage, it makes debugging difficult. Consider logging the exception with appropriate detail (avoiding sensitive information) to help with troubleshooting authentication issues.

Copilot uses AI. Check for mistakes.
.flatMap(auth -> {
String sub = extractSub(auth);

// JWT 검증은 SecurityWebFilterChain에서 이미 처리되었으므로, sub이 없으면 그대로 진행
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The comment is in Korean while code uses English. For consistency, code comments should be in English.

Copilot uses AI. Check for mistakes.
- OPTIONS
allowed-headers:
- "*"
allow-credentials: true
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The CORS configuration allows all origins with credentials enabled, which is a security vulnerability. When allow-credentials is true, allowed-origins cannot be "*" as this exposes the application to CSRF attacks from any origin. Either set specific origins for testing or set allow-credentials to false.

Suggested change
allow-credentials: true
allow-credentials: false

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +38
private Mono<Void> handleAuthenticationError(ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The authentication error handler returns a generic 401 Unauthorized status without a response body. This provides no information about why authentication failed, making it difficult for API consumers to debug issues. Consider adding an error message body with details about the authentication failure.

Copilot uses AI. Check for mistakes.
public boolean validateToken(String token) {
try {
Claims claims = parse(token);
return !claims.getExpiration().before(new Date());
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Missing null check for parsed claims before calling getExpiration(). If token parsing succeeds but the claims don't contain an expiration, this could throw a NullPointerException. The null check should be added or the exception should be explicitly handled.

Suggested change
return !claims.getExpiration().before(new Date());
Date expiration = claims.getExpiration();
if (expiration == null) {
return false;
}
return !expiration.before(new Date());

Copilot uses AI. Check for mistakes.
@Override
public void setAuthenticated(boolean isAuthenticated) {
if (isAuthenticated) {
throw new IllegalArgumentException("인증 상태 설정은 생성자에서만 할 수 있습니다.");
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The error message is in Korean while the rest of the codebase appears to use English. For consistency and international collaboration, error messages should be in English.

Suggested change
throw new IllegalArgumentException("인증 상태 설정은 생성자에서만 할 수 있습니다.");
throw new IllegalArgumentException("Authentication state can only be set in the constructor.");

Copilot uses AI. Check for mistakes.
}

public Long getMemberId(String token) {
return Long.parseLong(parse(token).getSubject());
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Potential uncaught 'java.lang.NumberFormatException'.

Suggested change
return Long.parseLong(parse(token).getSubject());
String subject = parse(token).getSubject();
if (subject == null) {
return null;
}
try {
return Long.parseLong(subject);
} catch (NumberFormatException e) {
return null;
}

Copilot uses AI. Check for mistakes.
@GoGradually GoGradually merged commit 364fd0a into master Dec 30, 2025
1 check passed
@GoGradually GoGradually deleted the feat/jwt-필터-추가 branch December 30, 2025 14:03
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.

2 participants