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
4 changes: 0 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,6 @@ dependencies {

// XSS 방어를 위한 HTML Sanitizer 라이브러리
implementation 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20220608.1'

// Redis 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'io.lettuce:lettuce-core'
}

// Query DSL 설정
Expand Down
57 changes: 0 additions & 57 deletions src/main/java/com/issueDive/config/RedisConfig.java

This file was deleted.

135 changes: 9 additions & 126 deletions src/main/java/com/issueDive/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package com.issueDive.controller;

import com.issueDive.entity.RefreshToken;
import com.issueDive.repository.RefreshTokenRepository;
import com.issueDive.dto.RefreshTokenRequest;
import com.issueDive.service.TokenBlacklistService;
import com.issueDive.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -17,14 +13,10 @@
import com.issueDive.service.UserService;
import jakarta.validation.Valid;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;

Expand All @@ -34,19 +26,11 @@
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Slf4j
public class
AuthController {
private final UserService userService;
private final JwtUtil jwtUtil;

@Autowired(required = false)
private final TokenBlacklistService tokenBlacklistService;

@Autowired(required = false) // 9월 10일 최종
private RefreshTokenRepository refreshTokenRepository;


@Operation(summary = "회원가입 및 자동 로그인", description = "새로운 사용자를 등록하고, 성공 시 즉시 로그인 처리하여 JWT를 발급합니다.")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "회원가입 및 자동 로그인 성공", content = @Content(mediaType = "application/json", schema = @Schema(implementation = JwtResponse.class))),
Expand Down Expand Up @@ -99,37 +83,13 @@ public ResponseEntity<ApiCommonResponse<JwtResponse>> login(@RequestBody(descrip
// JWT AccessToken만 생성 (RefreshToken 제거)
String accessToken = jwtUtil.generateAccessToken(userResponse.getId(), userResponse.getEmail());

//Refresh Token 생성 (Repository가 있을 때만)
String refreshToken = null;
if (refreshTokenRepository != null) {
try {
// 기존 토큰 삭제
refreshTokenRepository.deleteByUserEmail(userResponse.getEmail());

// 새 Refresh Token 생성
refreshToken = jwtUtil.generateRefreshToken(userResponse.getId(), userResponse.getEmail());

// DB에 저장
RefreshToken token = RefreshToken.builder()
.token(refreshToken)
.userId(userResponse.getId())
.userEmail(userResponse.getEmail())
.expiresAt(LocalDateTime.now().plusDays(7))
.build();
refreshTokenRepository.save(token);
} catch (Exception e) {
log.debug("9월 10일 최종 - Refresh Token 생성 실패 (무시): {}", e.getMessage());
}
}

// 응답 생성
JwtResponse jwtResponse = JwtResponse.builder()
.accessToken(accessToken)
.refreshToken(refreshToken) // 9월 10일 최종
.tokenType("Bearer")
.expiresIn(14400L)
.user(userResponse)
.build();
// JWT 응답 생성 (RefreshToken 제거)
JwtResponse jwtResponse = JwtResponse.of(
accessToken,
"Bearer",
14400L, // 9월1일 변경 - 4시간 (초 단위)
userResponse
);
return ResponseEntity.ok(ApiCommonResponse.ok(jwtResponse));
} catch (Exception e) {
//인증 실패시 예외 던지기 (GlobalExceptionHandler에서 처리)
Expand All @@ -143,30 +103,9 @@ public ResponseEntity<ApiCommonResponse<JwtResponse>> login(@RequestBody(descrip
@ApiResponse(responseCode = "200", description = "로그아웃 성공")
})
@PostMapping("/logout")
public ResponseEntity<ApiCommonResponse<Map<String, String>>> logout(@RequestHeader(value = "Authorization", required = false) String authHeader) {

// 9월 10일 최종 - 블랙리스트 처리
if (tokenBlacklistService != null && authHeader != null && authHeader.startsWith("Bearer ")) {
try {
String token = authHeader.substring(7);
public ResponseEntity<ApiCommonResponse<Map<String, String>>> logout() {
//JWT는 stateless하므로 서버에서 특별한 로그아웃 처리 불필요

// Access Token 블랙리스트 추가
Date expirationDate = jwtUtil.getExpirationDateFromToken(token);
tokenBlacklistService.addToBlacklist(token, expirationDate);

// Refresh Token 삭제
if (refreshTokenRepository != null) {
try {
String email = jwtUtil.getUserEmailFromToken(token);
refreshTokenRepository.deleteByUserEmail(email);
} catch (Exception e) {
log.debug("9월 10일 최종 - Refresh Token 삭제 실패 (무시)");
}
}
} catch (Exception e) {
log.error("9월 10일 최종 - 로그아웃 처리 중 오류 (무시): {}", e.getMessage());
}
}
Map<String, String> responseData = Map.of(
"message", "로그아웃되었습니다. 클라이언트에서 토큰을 삭제해주세요.",
"instruction", "localStorage에서 accessToken을 제거하세요."
Expand All @@ -176,62 +115,6 @@ public ResponseEntity<ApiCommonResponse<Map<String, String>>> logout(@RequestHea
return ResponseEntity.ok(response);
}

// 4. 9월 10일 최종 - refresh 엔드포인트 추가 (최소 기능)
@Operation(summary = "토큰 갱신", description = "Refresh Token으로 새 Access Token을 발급받습니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "토큰 갱신 성공"),
@ApiResponse(responseCode = "401", description = "유효하지 않은 Refresh Token")
})
@PostMapping("/refresh")
public ResponseEntity<ApiCommonResponse<JwtResponse>> refreshToken(
@RequestBody RefreshTokenRequest request) {

if (refreshTokenRepository == null) {
throw new com.issueDive.exception.AuthenticationFailedException("토큰 갱신 기능이 비활성화되어 있습니다.");
}

try {
String refreshToken = request.getRefreshToken();
if (refreshToken == null || refreshToken.isEmpty()) {
throw new com.issueDive.exception.AuthenticationFailedException("Refresh Token이 필요합니다.");
}

// DB에서 토큰 조회
RefreshToken storedToken = refreshTokenRepository.findByToken(refreshToken)
.orElseThrow(() -> new com.issueDive.exception.AuthenticationFailedException("유효하지 않은 Refresh Token"));

// 만료 확인
if (storedToken.getExpiresAt().isBefore(LocalDateTime.now())) {
// 만료된 토큰은 삭제
refreshTokenRepository.delete(storedToken);
throw new com.issueDive.exception.AuthenticationFailedException("만료된 Refresh Token");
}

// 사용자 정보 조회
UserResponseDTO user = userService.findUserById(storedToken.getUserId());

// 새 Access Token 생성
String newAccessToken = jwtUtil.generateAccessToken(user.getId(), user.getEmail());

// 응답 생성
JwtResponse jwtResponse = JwtResponse.builder()
.accessToken(newAccessToken)
.refreshToken(refreshToken) // 기존 Refresh Token 유지
.tokenType("Bearer")
.expiresIn(14400L)
.user(user)
.build();

return ResponseEntity.ok(ApiCommonResponse.ok(jwtResponse));

} catch (com.issueDive.exception.AuthenticationFailedException e) {
throw e;
} catch (Exception e) {
log.error("9월 10일 최종 - 토큰 갱신 중 오류: ", e);
throw new com.issueDive.exception.AuthenticationFailedException("토큰 갱신 실패");
}
}

@Operation(summary = "사용자 정보 조회", description = "ID로 특정 사용자의 정보를 조회합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공", content = @Content(mediaType = "application/json", schema = @Schema(implementation = UserResponseDTO.class))),
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/com/issueDive/dto/JwtResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@
@Builder
public class JwtResponse {
private String accessToken;
private String refreshToken;
private String tokenType;
private Long expiresIn;
private UserResponseDTO user;

public static JwtResponse of (String accessToken, String tokenType, Long expiresIn, UserResponseDTO user){
return JwtResponse.builder()
.accessToken(accessToken)
.refreshToken(null)
.tokenType(tokenType)
.expiresIn(expiresIn)
.user(user)
Expand Down
12 changes: 0 additions & 12 deletions src/main/java/com/issueDive/dto/RefreshTokenRequest.java

This file was deleted.

43 changes: 0 additions & 43 deletions src/main/java/com/issueDive/entity/RefreshToken.java

This file was deleted.

30 changes: 0 additions & 30 deletions src/main/java/com/issueDive/repository/RefreshTokenRepository.java

This file was deleted.

10 changes: 0 additions & 10 deletions src/main/java/com/issueDive/security/JwtAuthenticationFilter.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.issueDive.security;

import com.issueDive.service.TokenBlacklistService;
import com.issueDive.util.JwtUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
Expand All @@ -26,8 +24,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter{

private final JwtUtil jwtUtil;
private final CustomUserDetailsService userDetailsService;
@Autowired(required = false) // 9월 10일 최종
private TokenBlacklistService tokenBlacklistService; // 9월 10일 최종

@Override
protected void doFilterInternal(HttpServletRequest request,
Expand All @@ -39,12 +35,6 @@ protected void doFilterInternal(HttpServletRequest request,

if (jwt != null && SecurityContextHolder.getContext().getAuthentication() == null) {

// 9월 10일 최종 - 블랙리스트 체크 추가
if (tokenBlacklistService != null && tokenBlacklistService.isBlacklisted(jwt)) {
log.warn("9월 10일 최종 - 블랙리스트 토큰 사용 시도");
setErrorResponse(response, "토큰이 무효화되었습니다.");
return;
}
// JWT에서 이메일 추출
String email = jwtUtil.getUserEmailFromToken(jwt);

Expand Down
Loading