Compare commits
2 Commits
c07e7511d3
...
31aed4bda0
Author | SHA1 | Date | |
---|---|---|---|
31aed4bda0 | |||
438bfc3bc5 |
@@ -46,7 +46,7 @@ public class CommonCodeServiceImpl implements CommonCodeService {
|
||||
@Transactional
|
||||
public void updateGroupCode(String code, CommonGroupCodeDto groupCodeDto) {
|
||||
CommonGroupCode existingGroupCode = commonGroupCodeRepository.findByCode(code)
|
||||
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND, "그룹 코드를 찾을 수 없습니다: " + code));
|
||||
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_TARGET_NOT_FOUND, "그룹 코드를 찾을 수 없습니다: " + code));
|
||||
|
||||
commonGroupCodeMapper.updateCommonGroupCodeFromDto(groupCodeDto, existingGroupCode);
|
||||
commonGroupCodeRepository.save(existingGroupCode);
|
||||
|
@@ -33,6 +33,7 @@ public enum ApiResponseCode {
|
||||
|
||||
// 404 Not Found
|
||||
COMMON_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "리소스를 찾을 수 없습니다"),
|
||||
COMMON_TARGET_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "대상을 찾을 수 없습니다"),
|
||||
|
||||
// 405 Method Not Allowed
|
||||
COMMON_METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED.value(), "허용되지 않는 메소드입니다"),
|
||||
|
@@ -65,7 +65,7 @@ public class JwtTokenIssuanceFilter extends UsernamePasswordAuthenticationFilter
|
||||
|
||||
// 토큰 생성
|
||||
String accessToken = jwtUtils.createAccessToken(member.getUserId(), member.getRole().getValue());
|
||||
String refreshToken = jwtUtils.createRefreshToken(member.getUserId(), member.getRole().getValue());
|
||||
String refreshToken = jwtUtils.createRefreshToken(member.getUserId(), member.getRole().getValue(), httpUtils.getClientIp());
|
||||
|
||||
member.setRefreshToken(refreshToken);
|
||||
member.setLoginIp(httpUtils.getClientIp());
|
||||
|
@@ -3,6 +3,7 @@ package com.bio.bio_backend.global.filter;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.bio.bio_backend.global.utils.HttpUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
@@ -30,6 +31,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
public class JwtTokenValidationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final JwtUtils jwtUtils;
|
||||
private final HttpUtils httpUtils;
|
||||
private final MemberService memberService;
|
||||
private final Environment env;
|
||||
private final SecurityPathConfig securityPathConfig;
|
||||
@@ -66,52 +68,53 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Access Token이 없거나 만료된 경우, Refresh Token으로 갱신 시도
|
||||
if (refreshToken != null) {
|
||||
// 1. Refresh Token 유효성 검증
|
||||
if (!jwtUtils.isValidRefreshToken(refreshToken)) {
|
||||
log.warn("Refresh Token이 유효하지 않습니다. URI: {}", request.getRequestURI());
|
||||
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.JWT_TOKEN_EXPIRED));
|
||||
return;
|
||||
}
|
||||
|
||||
// Access Token이 없거나 만료된 경우, Refresh Token으로 갱신 시도
|
||||
if (refreshToken != null) {
|
||||
// 1. Refresh Token 유효성 검증
|
||||
if (!jwtUtils.isValidRefreshToken(refreshToken)) {
|
||||
log.warn("Refresh Token이 유효하지 않습니다. URI: {}", request.getRequestURI());
|
||||
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.JWT_TOKEN_EXPIRED));
|
||||
// 2. IP 주소 검증
|
||||
if (!jwtUtils.isValidClientIp(refreshToken, request.getRemoteAddr())) {
|
||||
log.warn("클라이언트 IP 주소가 일치하지 않습니다. URI: {}, IP: {}",
|
||||
request.getRequestURI(), request.getRemoteAddr());
|
||||
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.INVALID_CLIENT_IP));
|
||||
return;
|
||||
}
|
||||
|
||||
// 모든 검증을 통과한 경우 토큰 갱신 진행
|
||||
String username = jwtUtils.extractUsername(refreshToken);
|
||||
String role = jwtUtils.extractRole(refreshToken);
|
||||
|
||||
// 새로운 Access Token 생성
|
||||
String newAccessToken = jwtUtils.generateToken(username, role,
|
||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access"))));
|
||||
|
||||
// 새로운 Access Token을 응답 헤더에 설정
|
||||
response.setHeader("Authorization", "Bearer " + newAccessToken);
|
||||
|
||||
// Refresh Token 갱신
|
||||
String newRefreshToken = jwtUtils.createRefreshToken(username, role, httpUtils.getClientIp());
|
||||
jwtUtils.setRefreshTokenCookie(response, newRefreshToken);
|
||||
|
||||
// 인증 정보 설정
|
||||
UserDetails userDetails = memberService.loadUserByUsername(username);
|
||||
if (userDetails != null) {
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
|
||||
log.info("토큰 자동 갱신 성공: {}", username);
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. IP 주소 검증
|
||||
if (!jwtUtils.isValidClientIp(refreshToken, request.getRemoteAddr())) {
|
||||
log.warn("클라이언트 IP 주소가 일치하지 않습니다. URI: {}, IP: {}",
|
||||
request.getRequestURI(), request.getRemoteAddr());
|
||||
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.INVALID_CLIENT_IP));
|
||||
return;
|
||||
}
|
||||
|
||||
// 모든 검증을 통과한 경우 토큰 갱신 진행
|
||||
String username = jwtUtils.extractUsername(refreshToken);
|
||||
String role = jwtUtils.extractRole(refreshToken);
|
||||
|
||||
// 새로운 Access Token 생성
|
||||
String newAccessToken = jwtUtils.generateToken(username, role,
|
||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access"))));
|
||||
|
||||
// 새로운 Access Token을 응답 헤더에 설정
|
||||
response.setHeader("Authorization", "Bearer " + newAccessToken);
|
||||
|
||||
// Refresh Token 갱신
|
||||
String newRefreshToken = jwtUtils.refreshTokens(username, role);
|
||||
jwtUtils.setRefreshTokenCookie(response, newRefreshToken);
|
||||
|
||||
// 인증 정보 설정
|
||||
UserDetails userDetails = memberService.loadUserByUsername(username);
|
||||
if (userDetails != null) {
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
|
||||
log.info("토큰 자동 갱신 성공: {}", username);
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 토큰이 없거나 모두 유효하지 않은 경우
|
||||
|
@@ -46,7 +46,7 @@ public class WebSecurity {
|
||||
}
|
||||
|
||||
private JwtTokenValidationFilter getJwtTokenValidationFilter() {
|
||||
return new JwtTokenValidationFilter(jwtUtils, memberService, env, securityPathConfig);
|
||||
return new JwtTokenValidationFilter(jwtUtils, httpUtils, memberService, env, securityPathConfig);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -37,7 +37,6 @@ public class JwtUtils {
|
||||
|
||||
// Token 생성
|
||||
public String generateToken(String username, String role, long expirationTime) {
|
||||
|
||||
return Jwts.builder()
|
||||
.subject(username)
|
||||
.claim("role", role)
|
||||
@@ -47,10 +46,34 @@ public class JwtUtils {
|
||||
.compact();
|
||||
}
|
||||
|
||||
// Token 생성(IP 정보 포함)
|
||||
public String generateToken(String username, String role, String clientIp, long expirationTime) {
|
||||
return Jwts.builder()
|
||||
.subject(username)
|
||||
.claim("role", role)
|
||||
.claim("ip", clientIp) // IP 정보 추가
|
||||
.issuedAt(new Date(System.currentTimeMillis()))
|
||||
.expiration(new Date(System.currentTimeMillis() + expirationTime))
|
||||
.signWith(getSigningKey())
|
||||
.compact();
|
||||
}
|
||||
|
||||
// Access Token 생성
|
||||
public String createAccessToken(String username, String role) {
|
||||
long expirationTime = Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access")));
|
||||
return generateToken(username, role, expirationTime);
|
||||
}
|
||||
|
||||
// Refresh Token 생성 시 IP 정보 포함
|
||||
public String createRefreshToken(String username, String role, String clientIp) {
|
||||
long expirationTime = Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh")));
|
||||
return generateToken(username, role, clientIp, expirationTime);
|
||||
}
|
||||
|
||||
// Token 검증
|
||||
public Boolean validateAccessToken(String token) {
|
||||
try {
|
||||
return isTokenExpired(token);
|
||||
return isValidTokenExpired(token);
|
||||
} catch (io.jsonwebtoken.ExpiredJwtException e) {
|
||||
log.debug("Access Token 만료: {}", e.getMessage());
|
||||
return false;
|
||||
@@ -60,50 +83,17 @@ public class JwtUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh Token 생성 시 IP 정보 포함
|
||||
public String createRefreshToken(String username, String role, String clientIp) {
|
||||
return generateToken(username, role, clientIp,
|
||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh"))));
|
||||
}
|
||||
|
||||
// IP 정보를 포함한 토큰 생성
|
||||
public String generateToken(String username, String role, String clientIp, long expirationTime) {
|
||||
return Jwts.builder()
|
||||
.subject(username)
|
||||
.claim("role", role)
|
||||
.claim("ip", clientIp) // IP 정보 추가
|
||||
.issuedAt(new Date(System.currentTimeMillis()))
|
||||
.expiration(new Date(System.currentTimeMillis() + expirationTime))
|
||||
.signWith(getSigningKey())
|
||||
.compact();
|
||||
}
|
||||
|
||||
// IP 정보 추출
|
||||
public String extractClientIp(String token) {
|
||||
Claims claims = extractAllClaims(token);
|
||||
return claims.get("ip", String.class);
|
||||
}
|
||||
|
||||
// Refresh Token 검증 시 IP도 함께 검증
|
||||
public Boolean validateRefreshToken(String token, String clientIp) {
|
||||
// 1. 토큰 유효성 검증
|
||||
if (!isValidRefreshToken(token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. IP 주소 검증
|
||||
if (!isValidClientIp(token, clientIp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Refresh Token 유효성 검증 (토큰 일치, 만료 여부)
|
||||
public Boolean isValidRefreshToken(String token) {
|
||||
try {
|
||||
String savedToken = memberService.getRefreshToken(extractUsername(token));
|
||||
return savedToken.equals(token) && !isTokenExpired(token);
|
||||
return savedToken.equals(token) && isValidTokenExpired(token);
|
||||
} catch (Exception e) {
|
||||
log.debug("Refresh Token 검증 실패: {}", e.getMessage());
|
||||
return false;
|
||||
@@ -127,8 +117,9 @@ public class JwtUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTokenExpired(String token) {
|
||||
return !extractAllClaims(token).getExpiration().before(new Date());
|
||||
private boolean isValidTokenExpired(String token) {
|
||||
Date expiration = extractAllClaims(token).getExpiration();
|
||||
return !expiration.before(new Date());
|
||||
}
|
||||
|
||||
public String extractUsername(String token) {
|
||||
@@ -142,7 +133,6 @@ public class JwtUtils {
|
||||
}
|
||||
|
||||
public Claims extractAllClaims(String token) {
|
||||
|
||||
return Jwts.parser()
|
||||
.verifyWith(getSigningKey())
|
||||
.build()
|
||||
@@ -168,27 +158,6 @@ public class JwtUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Access Token 생성
|
||||
public String createAccessToken(String username, String role) {
|
||||
return generateToken(username, role,
|
||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access"))));
|
||||
}
|
||||
|
||||
// Refresh Token 생성
|
||||
public String createRefreshToken(String username, String role) {
|
||||
return generateToken(username, role,
|
||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh"))));
|
||||
}
|
||||
|
||||
// Refresh Token 갱신 (Access Token 갱신 시 함께)
|
||||
public String refreshTokens(String username, String role) {
|
||||
// 새로운 Refresh Token 생성 및 DB 저장
|
||||
String newRefreshToken = createRefreshToken(username, role);
|
||||
|
||||
log.info("Refresh Token 갱신 완료: {}", username);
|
||||
return newRefreshToken;
|
||||
}
|
||||
|
||||
// Refresh Token 쿠키 설정
|
||||
public void setRefreshTokenCookie(HttpServletResponse response, String refreshToken) {
|
||||
Cookie refreshTokenCookie = new Cookie("RefreshToken", refreshToken);
|
||||
|
Reference in New Issue
Block a user