Skip to content
Merged
Show file tree
Hide file tree
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
246 changes: 246 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# .github/workflows/cd.yml
name: CD - Deploy to EC2 (Blue-Green)

on:
push: # Push 이벤트 발생 시
branches: [ main ] # main 브랜치에 Push될 때만 실행
workflow_dispatch: # 수동 실행 가능

env: # 워크플로우 전체에서 사용할 환경 변수
AWS_REGION: ap-northeast-2 # 본인이 사용하는 AWS 리전으로 변경
ECR_REPOSITORY: focussu-backend # 생성한 ECR 리포지토리 이름

jobs:
# --- Job 1: Docker 이미지 빌드 및 ECR 푸시 ---
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
outputs: # 다음 job('deploy-blue-green')에서 사용할 값 정의
image_tag: ${{ steps.determine_tag.outputs.tag }} # 생성된 이미지 태그 전달

steps:
# 1. 코드 체크아웃
- name: Checkout code
uses: actions/checkout@v4

# 2. AWS 자격 증명 설정: GitHub Secrets에 저장된 AWS 키를 사용하여 AWS 서비스에 접근할 권한 설정
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} # GitHub Secret
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # GitHub Secret
aws-region: ${{ env.AWS_REGION }}

# 3. Amazon ECR 로그인: Docker 클라이언트가 ECR에 이미지를 푸시할 수 있도록 로그인
- name: Login to Amazon ECR
id: login-ecr # 이 스텝의 출력을 다른 스텝에서 사용할 수 있도록 ID 부여
uses: aws-actions/amazon-ecr-login@v2

# 4. JDK 설정
- name: Set up JDK 17
uses: actions/setup-java@v4
with: { java-version: '17', distribution: 'temurin', cache: 'gradle' }

# 5. Gradle 설정
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3

# 6. gradlew 실행 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
working-directory: ./backend

# 7. Spring Boot JAR 빌드 (테스트 제외): CI 단계에서 이미 테스트를 통과했으므로 '-x test' 옵션으로 테스트 생략
- name: Build Spring Boot JAR (without tests)
run: ./gradlew bootJar -x test
working-directory: ./backend

# 8. 이미지 태그 결정: GitHub Commit SHA의 앞 7자리를 이미지 태그로 사용 (고유성 확보)
- name: Determine Image Tag (use commit SHA)
id: determine_tag
run: echo "tag=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT # GitHub Actions 출력 변수로 설정

# 9. Docker 이미지 빌드 및 태그 지정: Dockerfile을 사용하여 이미지 빌드하고 ECR 주소와 태그 지정
- name: Build and tag Docker image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} # ECR 로그인 스텝에서 얻은 레지스트리 주소
IMAGE_TAG: ${{ steps.determine_tag.outputs.tag }} # 위에서 결정한 이미지 태그
run: |
# docker build 명령어 실행
# -t 옵션: 이미지 이름 및 태그 지정 (예: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/focussu-backend:abcdefg)
# ./backend: Dockerfile이 있는 디렉토리 경로
# --build-arg: Dockerfile 내에서 사용할 변수 전달 (JAR 파일 경로)
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG ./backend \
--build-arg JAR_FILE=build/libs/*.jar
# 10. Docker 이미지를 ECR로 푸시
- name: Push Docker image to ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ steps.determine_tag.outputs.tag }}
run: docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

# --- Job 2: EC2에 Blue-Green 배포 ---
deploy-blue-green:
name: Deploy to EC2 (Blue-Green)
needs: build-and-push # 'build-and-push' job이 성공해야만 실행됨
runs-on: ubuntu-latest

steps:
# 1. AWS 자격 증명 설정 (EC2에서 ECR 접근 등에 필요할 수 있음)
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with: { aws-access-key-id: ${ { secrets.AWS_ACCESS_KEY_ID } }, aws-secret-access-key: ${ { secrets.AWS_SECRET_ACCESS_KEY } }, aws-region: ${ { env.AWS_REGION } } }

Comment on lines +91 to +94
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

YAML 구문 오류 수정 필요

with: { aws-access-key-id: ${ { secrets.AWS_ACCESS_KEY_ID } }, ... } 부분에서 ${ { ... } } 공백으로 인해 YAML 파싱 오류가 발생합니다. 아래처럼 블록 매핑으로 변경하거나 ${{ ... }} 문법을 올바르게 사용하세요.

-      - name: Configure AWS credentials
-        uses: aws-actions/configure-aws-credentials@v4
-        with: { aws-access-key-id: ${ { secrets.AWS_ACCESS_KEY_ID } }, aws-secret-access-key: ${ { secrets.AWS_SECRET_ACCESS_KEY } }, aws-region: ${ { env.AWS_REGION } } }
+      - name: Configure AWS credentials
+        uses: aws-actions/configure-aws-credentials@v4
+        with:
+          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+          aws-region: ${{ env.AWS_REGION }}
🧰 Tools
🪛 actionlint (1.7.4)

92-92: could not parse as YAML: yaml: line 92: did not find expected ',' or '}'

(syntax-check)

🪛 YAMLlint (1.35.1)

[error] 93-93: syntax error: expected ',' or '}', but got '{'

(syntax)

# 2. Amazon ECR 로그인 (EC2 인스턴스에서 이미지를 pull 할 때 필요할 수 있음)
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2

# 3. SSH를 통해 EC2 서버에 접속하여 배포 스크립트 실행
- name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.3 # 검증된 버전 사용 권장
with:
host: ${{ secrets.EC2_HOST }} # EC2 Public IP 또는 DNS (GitHub Secret)
username: ubuntu # EC2 사용자 이름 (AMI에 따라 다름)
key: ${{ secrets.EC2_SSH_KEY }} # EC2 접속용 Private Key (GitHub Secret)
port: 22 # SSH 포트
script_stop: true # 스크립트 실행 중 오류 발생 시 즉시 중지
script: |
# --- EC2 서버 내부에서 실행될 스크립트 시작 ---
# 0. 환경 변수 설정 (스크립트 내에서 사용)
export ECR_REGISTRY=${{ steps.login-ecr.outputs.registry }} # 이전 스텝에서 얻은 ECR 주소
export ECR_REPOSITORY=${{ env.ECR_REPOSITORY }} # 워크플로우 환경 변수
export IMAGE_TAG=${{ needs.build-and-push.outputs.image_tag }} # 이전 job에서 전달된 이미지 태그
export APP_DIR="/home/ubuntu/app" # EC2 내 작업 디렉토리 경로
# 1. 배포 대상 결정 (Blue 또는 Green)
cd $APP_DIR # 작업 디렉토리로 이동
# 현재 Nginx가 가리키는 서비스 확인 (service-env.inc 파일 읽기)
CURRENT_UPSTREAM_URL=$(cat $APP_DIR/service-env.inc | grep -o 'http://[^;]*')
echo "Current Nginx upstream URL: $CURRENT_UPSTREAM_URL"
# 현재 서비스가 blue이면 타겟은 green, 아니면 blue
if [[ "$CURRENT_UPSTREAM_URL" == *"backend-blue"* ]]; then
CURRENT_SERVICE="backend-blue"
TARGET_SERVICE="backend-green"
TARGET_PORT="8081"
TARGET_IMAGE_TAG="green" # docker-compose.yml에서 사용할 이미지 태그
CURRENT_IMAGE_TAG="blue"
else
CURRENT_SERVICE="backend-green"
TARGET_SERVICE="backend-blue"
TARGET_PORT="8080"
TARGET_IMAGE_TAG="blue"
CURRENT_IMAGE_TAG="green"
fi
echo "Deployment Target Service: $TARGET_SERVICE (Port: $TARGET_PORT), Target Image Tag: $TARGET_IMAGE_TAG"
echo "Current Live Service: $CURRENT_SERVICE, Current Image Tag: $CURRENT_IMAGE_TAG"
# 2. 환경변수 파일 생성 (.env) - 애플리케이션 및 Compose에서 사용
# GitHub Secrets 값을 EC2 서버의 .env 파일로 안전하게 전달
# 주의: 이 파일은 보안상 중요하므로 접근 권한 관리가 필요하며, gitignore 처리 필수
echo "Creating .env file in $APP_DIR..."
echo "RDS_ENDPOINT=${{ secrets.RDS_ENDPOINT }}" > $APP_DIR/.env
echo "RDS_PORT=${{ secrets.RDS_PORT }}" >> $APP_DIR/.env
echo "RDS_DATABASE=${{ secrets.RDS_DATABASE }}" >> $APP_DIR/.env
echo "RDS_USERNAME=${{ secrets.RDS_USERNAME }}" >> $APP_DIR/.env
echo "RDS_PASSWORD=${{ secrets.RDS_PASSWORD }}" >> $APP_DIR/.env
echo "ELASTICACHE_ENDPOINT=${{ secrets.ELASTICACHE_ENDPOINT }}" >> $APP_DIR/.env
echo "ELASTICACHE_PORT=${{ secrets.ELASTICACHE_PORT }}" >> $APP_DIR/.env
echo "JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }}" >> $APP_DIR/.env
echo "JWT_EXPIRATION_TIME=${{ secrets.JWT_EXPIRATION_TIME }}" >> $APP_DIR/.env
# docker-compose.yml에서 이미지 주소를 동적으로 사용하기 위한 변수 추가
echo "ECR_REGISTRY=$ECR_REGISTRY" >> $APP_DIR/.env
echo "ECR_REPOSITORY=$ECR_REPOSITORY" >> $APP_DIR/.env
echo ".env file created successfully."
# 3. 최신 Docker 이미지 Pull 및 리태깅
echo "Pulling new image: $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
# ECR 로그인 (권한 만료 대비, aws-cli가 EC2에 설치되어 있어야 함) - 선택적
# aws ecr get-login-password --region ${{ env.AWS_REGION }} | docker login --username AWS --password-stdin $ECR_REGISTRY
docker pull $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
# docker-compose.yml에서 사용할 태그(blue 또는 green)로 다시 태그 지정
echo "Tagging image as $ECR_REGISTRY/$ECR_REPOSITORY:$TARGET_IMAGE_TAG..."
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:$TARGET_IMAGE_TAG
# 4. 새 버전 컨테이너 실행 (Target Service)
# docker-compose는 자동으로 .env 파일을 로드하여 환경 변수 설정
# --env-file 옵션으로 명시적으로 지정하여 안정성 확보
echo "Starting $TARGET_SERVICE container with image $ECR_REGISTRY/$ECR_REPOSITORY:$TARGET_IMAGE_TAG..."
# --no-deps: 의존성 관계에 있는 다른 서비스는 건드리지 않음
# --remove-orphans: docker-compose 파일에서 제거된 서비스의 컨테이너 삭제
docker-compose -f $APP_DIR/docker-compose-prod.yml --env-file $APP_DIR/.env up -d --no-deps --remove-orphans $TARGET_SERVICE
echo "$TARGET_SERVICE starting process initiated."
# 5. Health Check 대기 (매우 중요!)
echo "Waiting for $TARGET_SERVICE health check (Target Port: $TARGET_PORT)... Max wait 300 seconds."
# timeout 명령어로 전체 대기 시간 제한 (예: 5분)
# bash -c '...' : 내부 스크립트를 별도의 쉘에서 실행
timeout 300s bash -c ' \
HEALTH_CHECK_PASSED=false; \
# 최대 30번 시도 (약 5분간 시도)
for i in {1..30}; do \
# EC2 내부에서 localhost와 타겟 포트로 헬스체크 URL 호출
# -s: silent 모드, -o /dev/null: 출력 버리기, -w "%{http_code}": HTTP 상태 코드만 출력
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$TARGET_PORT/actuator/health); \
# 상태 코드가 200이면 성공
if [ "$response" = "200" ]; then \
echo "$TARGET_SERVICE is healthy! (Attempt $i)"; \
HEALTH_CHECK_PASSED=true; \
break; # 성공 시 루프 종료
fi; \
# 실패 시 로그 출력 후 10초 대기
echo "Health check attempt $i failed (HTTP Code: $response). Retrying in 10 seconds..."; \
sleep 10; \
done; \
# 루프 종료 후 성공 여부 확인
if [ "$HEALTH_CHECK_PASSED" = false ]; then \
echo "Health check failed for $TARGET_SERVICE after multiple attempts."; \
exit 1; # 실패 시 스크립트 비정상 종료 (exit code 1)
fi \
'
HEALTH_CHECK_EXIT_CODE=$? # timeout 또는 내부 스크립트의 종료 코드 확인
# 6. Health Check 실패 또는 타임아웃 시 롤백
if [ "$HEALTH_CHECK_EXIT_CODE" -ne 0 ]; then
echo "Health check failed or timed out (Exit Code: $HEALTH_CHECK_EXIT_CODE). Rolling back deployment..."
# 실패한 타겟 서비스 컨테이너 중지
docker-compose -f $APP_DIR/docker-compose-prod.yml stop $TARGET_SERVICE
# docker-compose -f $APP_DIR/docker-compose-prod.yml rm -f $TARGET_SERVICE # 컨테이너 삭제는 선택적
# Optional: 실패 알림 전송 (예: Slack 웹훅 호출)
exit 1 # GitHub Actions 워크플로우를 실패 상태로 만듦
fi
# --- Health Check 성공 시 계속 진행 ---
# 7. Nginx 트래픽 전환
echo "Switching Nginx traffic to $TARGET_SERVICE..."
# service-env.inc 파일 내용을 타겟 서비스 URL로 변경 (sudo 필요)
echo "set \$service_url http://$TARGET_SERVICE:$TARGET_PORT;" | sudo tee $APP_DIR/service-env.inc
# Nginx 컨테이너 내부에서 nginx reload 명령어 실행 (설정 다시 로드)
docker exec nginx-proxy nginx -s reload
echo "Nginx reloaded. Traffic is now directed to $TARGET_SERVICE."
sleep 5 # 트래픽 전환 후 안정화 대기 시간
# 8. 이전 버전 컨테이너 중지 (Current Service)
echo "Stopping old service: $CURRENT_SERVICE..."
docker-compose -f $APP_DIR/docker-compose-prod.yml stop $CURRENT_SERVICE
echo "$CURRENT_SERVICE stopped."
# 9. (선택 사항) 사용하지 않는 이전 버전 Docker 이미지 태그 삭제
# echo "Removing old image tag if different: $ECR_REGISTRY/$ECR_REPOSITORY:$CURRENT_IMAGE_TAG"
# # 현재 태그와 새 태그가 다를 경우에만 삭제 시도
# if [ "$CURRENT_IMAGE_TAG" != "$TARGET_IMAGE_TAG" ]; then
# docker rmi $ECR_REGISTRY/$ECR_REPOSITORY:$CURRENT_IMAGE_TAG || echo "Old image tag removal failed (maybe already removed or in use)."
# fi
# 10. (선택 사항, 주의!) 사용하지 않는 Docker 리소스 정리
echo "Cleaning up unused Docker resources (dangling images)..."
docker image prune -af # 태그가 없는(dangling) 이미지 모두 삭제
echo "Blue-Green Deployment to $TARGET_SERVICE Completed Successfully!"
# --- EC2 서버 내부에서 실행될 스크립트 종료 ---
48 changes: 48 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# .github/workflows/ci.yml
name: CI - Build and Test

on:
pull_request: # Pull Request 이벤트 발생 시
branches: [ main ] # main 브랜치를 대상으로 하는 PR
workflow_dispatch: # 수동 실행도 가능하게

jobs:
build-test:
name: Build and Run Tests
runs-on: ubuntu-latest # 실행 환경 지정

steps:
# 1. 코드 체크아웃: 리포지토리의 코드를 워크플로우 실행 환경으로 가져옵니다.
- name: Checkout code
uses: actions/checkout@v4

# 2. JDK 설정: 빌드 및 테스트에 사용할 Java 버전(17)을 설정합니다.
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin' # Temurin JDK 사용
cache: 'gradle' # Gradle 의존성 캐싱으로 빌드 시간 단축

# 3. Gradle 설정: Gradle 빌드 환경을 설정합니다.
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
# with:
# gradle-version: wrapper # 프로젝트의 Gradle Wrapper 사용을 명시 (선택 사항)

# 4. gradlew 실행 권한 부여: Gradle Wrapper 스크립트에 실행 권한을 줍니다.
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
working-directory: ./backend # backend 디렉토리에서 실행
Comment on lines +28 to +36
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

chmod 단계를 Setup Gradle 이전으로 이동하는 것을 권장합니다

gradle/actions/setup-gradle@v3는 내부적으로 ./gradlew를 호출할 수 있습니다. 현재 순서에서는 gradlew의 실행 권한이 아직 부여되지 않은 상태이므로, OS에 따라 퍼미션 오류가 발생할 가능성이 있습니다. 안전하게 권한 부여를 먼저 수행한 뒤 Gradle 설정을 실행하는 편이 좋습니다.

-      - name: Setup Gradle
-        uses: gradle/actions/setup-gradle@v3
+      - name: Grant execute permission for gradlew
+        run: chmod +x ./gradlew
+        working-directory: ./backend
+
+      - name: Setup Gradle
+        uses: gradle/actions/setup-gradle@v3

Committable suggestion skipped: line range outside the PR's diff.


# 5. Gradle 빌드 및 테스트: Gradle의 build 태스크를 실행합니다. 이 태스크는 컴파일, 테스트 실행 등을 포함합니다.
- name: Build and Test with Gradle
run: ./gradlew build # 'build' 태스크는 기본적으로 'test' 태스크를 포함하여 실행합니다.
working-directory: ./backend

# (선택 사항) Dockerfile 빌드 가능성 테스트 (실제 푸시는 하지 않음)
# - name: Build Docker image (test only)
# run: |
# # Dockerfile이 정상적으로 빌드되는지만 확인
# docker build -t ci-build-test ./backend \
# --build-arg JAR_FILE=build/libs/*.jar
4 changes: 0 additions & 4 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ dependencies {
// docs
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'

//kafka
implementation 'org.springframework.kafka:spring-kafka'

// jwt
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
Expand All @@ -53,7 +50,6 @@ dependencies {
// test
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.kafka:spring-kafka-test'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void login(

@Operation(
summary = "로그아웃",
description = "로그아웃 요청을 처리하는 필터(LogoutFilter)에서 동작합니다."
description = "로그아웃 요청(LogoutHandler)에서 동작합니다."
)
@ApiResponses({
@ApiResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
Expand All @@ -25,6 +26,7 @@

import java.io.IOException;

@Slf4j
@Component
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
private final ObjectMapper mapper = new ObjectMapper();
Expand Down Expand Up @@ -63,15 +65,26 @@ protected void successfulAuthentication(HttpServletRequest req,
FilterChain chain,
Authentication auth)
throws IOException, ServletException {

// 1. 인증 성공 후 사용자 정보 가져오기
String username = auth.getName();
// 반드시 UserDetails 로드해서 토큰 생성
UserDetails user = userDetailsService.loadUserByUsername(username);
String jwt = jwtTokenUtil.generateToken(user);
tokenService.saveToken(jwt, user.getUsername());

// 2. 새로운 JWT 토큰 생성
String newJwt = jwtTokenUtil.generateToken(user);

// 3. (추가) Redis에서 해당 사용자의 기존 토큰 삭제
tokenService.removeTokenByUsername(user.getUsername());

// 4. 새로운 토큰을 Redis에 저장
tokenService.saveToken(newJwt, user.getUsername());
log.info("[LOGIN FILTER] Save {} Token..", user.getUsername());

// 5. 응답 설정
res.setCharacterEncoding("UTF-8");
res.addHeader("Authorization", "Bearer " + jwt);
res.addHeader("Authorization", "Bearer " + newJwt);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(res.getWriter(), new AuthenticationResponse(jwt));
mapper.writeValue(res.getWriter(), new AuthenticationResponse(newJwt));
}
Comment on lines +68 to 88
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

SecurityContext 미설정으로 인증 정보 유실 가능

successfulAuthentication에서 SecurityContextHolder.getContext().setAuthentication(auth) 호출이나
super.successfulAuthentication(req, res, chain, auth) 위임이 빠져 있습니다.
로그인 직후 같은 요청 흐름(예: 후속 필터, 이벤트 퍼블리셔)에서 인증 객체가 필요하다면 현재 구현으로는 null 이 될 수 있습니다.

@@
-        // 5. 응답 설정
+        // 5. SecurityContext 저장
+        SecurityContextHolder.getContext().setAuthentication(auth);
+
+        // 6. 응답 설정
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 1. 인증 성공 후 사용자 정보 가져오기
String username = auth.getName();
// 반드시 UserDetails 로드해서 토큰 생성
UserDetails user = userDetailsService.loadUserByUsername(username);
String jwt = jwtTokenUtil.generateToken(user);
tokenService.saveToken(jwt, user.getUsername());
// 2. 새로운 JWT 토큰 생성
String newJwt = jwtTokenUtil.generateToken(user);
// 3. (추가) Redis에서 해당 사용자의 기존 토큰 삭제
tokenService.removeTokenByUsername(user.getUsername());
// 4. 새로운 토큰을 Redis에 저장
tokenService.saveToken(newJwt, user.getUsername());
log.info("[LOGIN FILTER] Save {} Token..", user.getUsername());
// 5. 응답 설정
res.setCharacterEncoding("UTF-8");
res.addHeader("Authorization", "Bearer " + jwt);
res.addHeader("Authorization", "Bearer " + newJwt);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(res.getWriter(), new AuthenticationResponse(jwt));
mapper.writeValue(res.getWriter(), new AuthenticationResponse(newJwt));
}
// 1. 인증 성공 후 사용자 정보 가져오기
String username = auth.getName();
UserDetails user = userDetailsService.loadUserByUsername(username);
// 2. 새로운 JWT 토큰 생성
String newJwt = jwtTokenUtil.generateToken(user);
// 3. (추가) Redis에서 해당 사용자의 기존 토큰 삭제
tokenService.removeTokenByUsername(user.getUsername());
// 4. 새로운 토큰을 Redis에 저장
tokenService.saveToken(newJwt, user.getUsername());
log.info("[LOGIN FILTER] Save {} Token..", user.getUsername());
// 5. SecurityContext 저장
SecurityContextHolder.getContext().setAuthentication(auth);
// 6. 응답 설정
res.setCharacterEncoding("UTF-8");
res.addHeader("Authorization", "Bearer " + newJwt);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(res.getWriter(), new AuthenticationResponse(newJwt));
}


@Override
Expand Down
Loading
Loading