Fix : 수정

This commit is contained in:
lsh9672 2025-06-12 17:40:22 +09:00
parent ef87bfbb0b
commit 0b78427304
5 changed files with 93 additions and 214 deletions

View File

@ -1,57 +1,59 @@
package com.ktds.hi.member.config;
import com.ktds.hi.member.service.JwtTokenProvider;
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.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
/**
* JWT 인증 필터 클래스
* 요청 헤더의 JWT 토큰을 검증하고 인증 정보를 설정
*/
@RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String token = resolveToken(request);
if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
log.debug("JWT 토큰 인증 성공: {}", authentication.getName());
}
} catch (Exception e) {
log.error("JWT 토큰 인증 실패", e);
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
/**
* 요청 헤더에서 JWT 토큰 추출
*/
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
// package com.ktds.hi.member.config;
//
//
// 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.security.core.Authentication;
// import org.springframework.security.core.context.SecurityContextHolder;
// import org.springframework.util.StringUtils;
// import org.springframework.web.filter.OncePerRequestFilter;
//
// import java.io.IOException;
//
// import com.ktds.hi.common.security.JwtTokenProvider;
//
// /**
// * JWT 인증 필터 클래스
// * 요청 헤더의 JWT 토큰을 검증하고 인증 정보를 설정
// */
// @RequiredArgsConstructor
// @Slf4j
// public class JwtAuthenticationFilter extends OncePerRequestFilter {
//
// private final JwtTokenProvider tokenProvider;
//
// @Override
// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
// FilterChain filterChain) throws ServletException, IOException {
//
// try {
// String token = resolveToken(request);
//
// if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
// Authentication authentication = tokenProvider.getAuthentication(token);
// SecurityContextHolder.getContext().setAuthentication(authentication);
// log.debug("JWT 토큰 인증 성공: {}", authentication.getName());
// }
// } catch (Exception e) {
// log.error("JWT 토큰 인증 실패", e);
// SecurityContextHolder.clearContext();
// }
//
// filterChain.doFilter(request, response);
// }
//
// /**
// * 요청 헤더에서 JWT 토큰 추출
// */
// private String resolveToken(HttpServletRequest request) {
// String bearerToken = request.getHeader("Authorization");
// if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
// return bearerToken.substring(7);
// }
// return null;
// }
// }

View File

@ -1,8 +1,9 @@
package com.ktds.hi.member.config;
import com.ktds.hi.member.config.JwtAuthenticationFilter;
import com.ktds.hi.member.service.JwtTokenProvider;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ktds.hi.common.security.JwtTokenProvider;
import com.ktds.hi.common.security.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
@ -26,7 +27,6 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
@RequiredArgsConstructor
public class SecurityConfig {
@Qualifier("memberJwtTokenProvider")
private final JwtTokenProvider jwtTokenProvider;
/**
@ -54,7 +54,7 @@ public class SecurityConfig {
*/
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter(jwtTokenProvider);
return new JwtAuthenticationFilter(jwtTokenProvider,new ObjectMapper());
}
/**

View File

@ -1,5 +1,6 @@
package com.ktds.hi.member.service;
import com.ktds.hi.common.security.JwtTokenProvider;
import com.ktds.hi.member.dto.*;
import com.ktds.hi.member.repository.entity.MemberEntity;
import com.ktds.hi.member.repository.jpa.MemberRepository;
@ -11,6 +12,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
/**
@ -24,7 +26,6 @@ public class AuthServiceImpl implements AuthService {
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
@Qualifier("memberJwtTokenProvider")
private final JwtTokenProvider jwtTokenProvider;
private final SmsService smsService;
private final RedisTemplate<String, String> redisTemplate;
@ -41,9 +42,13 @@ public class AuthServiceImpl implements AuthService {
}
// JWT 토큰 생성
String accessToken = jwtTokenProvider.generateAccessToken(member.getId(), member.getRole());
String refreshToken = jwtTokenProvider.generateRefreshToken(member.getId());
String accessToken = jwtTokenProvider.createAccessToken(
member.getId().toString(),
Collections.singletonList(member.getRole())
);
String refreshToken = jwtTokenProvider.createRefreshToken(member.getId().toString());
// 리프레시 토큰 Redis 저장
redisTemplate.opsForValue().set(
"refresh_token:" + member.getId(),
@ -62,10 +67,12 @@ public class AuthServiceImpl implements AuthService {
@Override
public void logout(LogoutRequest request) {
// 리프레시 토큰에서 사용자 ID 추출
Long memberId = jwtTokenProvider.getMemberIdFromToken(request.getRefreshToken());
// Redis에서 리프레시 토큰 삭제
redisTemplate.delete("refresh_token:" + memberId);
String userId = jwtTokenProvider.getUserIdFromToken(request.getRefreshToken());
if (userId != null) {
// Redis에서 리프레시 토큰 삭제
redisTemplate.delete("refresh_token:" + userId);
}
}
@Override
@ -75,7 +82,7 @@ public class AuthServiceImpl implements AuthService {
throw new BusinessException("유효하지 않은 리프레시 토큰입니다");
}
Long memberId = jwtTokenProvider.getMemberIdFromToken(refreshToken);
Long memberId = Long.parseLong(jwtTokenProvider.getUserIdFromToken(refreshToken));
// Redis에서 리프레시 토큰 확인
String storedToken = redisTemplate.opsForValue().get("refresh_token:" + memberId);
@ -88,8 +95,12 @@ public class AuthServiceImpl implements AuthService {
.orElseThrow(() -> new BusinessException("존재하지 않는 사용자입니다"));
// 토큰 생성
String newAccessToken = jwtTokenProvider.generateAccessToken(member.getId(), member.getRole());
String newRefreshToken = jwtTokenProvider.generateRefreshToken(member.getId());
String newAccessToken = jwtTokenProvider.createAccessToken(
member.getId().toString(),
Collections.singletonList(member.getRole())
);
String newRefreshToken = jwtTokenProvider.createRefreshToken(member.getId().toString());
// 리프레시 토큰 Redis 저장
redisTemplate.opsForValue().set(

View File

@ -1,139 +0,0 @@
package com.ktds.hi.member.service;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Collections;
import java.util.Date;
/**
* JWT 토큰 프로바이더 클래스
* JWT 토큰 생성, 검증, 파싱 기능을 제공
*/
@Component("memberJwtTokenProvider")
@Slf4j
public class JwtTokenProvider {
private final SecretKey secretKey;
private final long accessTokenExpiration;
private final long refreshTokenExpiration;
public JwtTokenProvider(@Value("${jwt.secret:mySecretKey123456789012345678901234567890}") String secret,
@Value("${jwt.access-token-expiration:1800000}") long accessTokenExpiration,
@Value("${jwt.refresh-token-expiration:604800000}") long refreshTokenExpiration) {
this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
this.accessTokenExpiration = accessTokenExpiration;
this.refreshTokenExpiration = refreshTokenExpiration;
}
/**
* 액세스 토큰 생성
*/
public String generateAccessToken(Long memberId, String role) {
Date now = new Date();
Date expiration = new Date(now.getTime() + accessTokenExpiration);
return Jwts.builder()
.setSubject(memberId.toString())
.claim("role", role)
.claim("type", "access")
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(secretKey)
.compact();
}
/**
* 리프레시 토큰 생성
*/
public String generateRefreshToken(Long memberId) {
Date now = new Date();
Date expiration = new Date(now.getTime() + refreshTokenExpiration);
return Jwts.builder()
.setSubject(memberId.toString())
.claim("type", "refresh")
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(secretKey)
.compact();
}
/**
* 토큰에서 인증 정보 추출
*/
public Authentication getAuthentication(String token) {
Claims claims = getClaims(token);
String memberId = claims.getSubject();
String role = (String) claims.get("role");
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + (role != null ? role : "USER"));
return new UsernamePasswordAuthenticationToken(
memberId,
null,
Collections.singletonList(authority)
);
}
/**
* 토큰 유효성 검증
*/
public boolean validateToken(String token) {
try {
getClaims(token);
return true;
} catch (ExpiredJwtException e) {
log.warn("JWT 토큰 만료: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
log.warn("지원되지 않는 JWT 토큰: {}", e.getMessage());
} catch (MalformedJwtException e) {
log.warn("잘못된 형식의 JWT 토큰: {}", e.getMessage());
} catch (SecurityException | IllegalArgumentException e) {
log.warn("JWT 토큰 검증 실패: {}", e.getMessage());
}
return false;
}
/**
* 토큰에서 회원 ID 추출
*/
public Long getMemberIdFromToken(String token) {
Claims claims = getClaims(token);
return Long.parseLong(claims.getSubject());
}
/**
* 토큰에서 클레임 추출
*/
private Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
}
/**
* 토큰 만료 시간 조회
*/
public Date getExpirationDateFromToken(String token) {
Claims claims = getClaims(token);
return claims.getExpiration();
}
/**
* 토큰이 만료되었는지 확인
*/
public boolean isTokenExpired(String token) {
Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
}

View File

@ -1,6 +1,11 @@
dependencies {
implementation project(':common')
// File Storage
// AI and Location Services
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0'
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j:'
implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer:latest'
}