2 Commits

6 changed files with 79 additions and 106 deletions

View File

@@ -46,7 +46,7 @@ public class CommonCodeServiceImpl implements CommonCodeService {
@Transactional @Transactional
public void updateGroupCode(String code, CommonGroupCodeDto groupCodeDto) { public void updateGroupCode(String code, CommonGroupCodeDto groupCodeDto) {
CommonGroupCode existingGroupCode = commonGroupCodeRepository.findByCode(code) 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); commonGroupCodeMapper.updateCommonGroupCodeFromDto(groupCodeDto, existingGroupCode);
commonGroupCodeRepository.save(existingGroupCode); commonGroupCodeRepository.save(existingGroupCode);

View File

@@ -33,6 +33,7 @@ public enum ApiResponseCode {
// 404 Not Found // 404 Not Found
COMMON_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "리소스를 찾을 수 없습니다"), COMMON_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "리소스를 찾을 수 없습니다"),
COMMON_TARGET_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "대상을 찾을 수 없습니다"),
// 405 Method Not Allowed // 405 Method Not Allowed
COMMON_METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED.value(), "허용되지 않는 메소드입니다"), COMMON_METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED.value(), "허용되지 않는 메소드입니다"),

View File

@@ -65,7 +65,7 @@ public class JwtTokenIssuanceFilter extends UsernamePasswordAuthenticationFilter
// 토큰 생성 // 토큰 생성
String accessToken = jwtUtils.createAccessToken(member.getUserId(), member.getRole().getValue()); 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.setRefreshToken(refreshToken);
member.setLoginIp(httpUtils.getClientIp()); member.setLoginIp(httpUtils.getClientIp());

View File

@@ -3,6 +3,7 @@ package com.bio.bio_backend.global.filter;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
import com.bio.bio_backend.global.utils.HttpUtils;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -30,6 +31,7 @@ import lombok.extern.slf4j.Slf4j;
public class JwtTokenValidationFilter extends OncePerRequestFilter { public class JwtTokenValidationFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils; private final JwtUtils jwtUtils;
private final HttpUtils httpUtils;
private final MemberService memberService; private final MemberService memberService;
private final Environment env; private final Environment env;
private final SecurityPathConfig securityPathConfig; private final SecurityPathConfig securityPathConfig;
@@ -66,8 +68,7 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
return; return;
} }
} } else {
// Access Token이 없거나 만료된 경우, Refresh Token으로 갱신 시도 // Access Token이 없거나 만료된 경우, Refresh Token으로 갱신 시도
if (refreshToken != null) { if (refreshToken != null) {
// 1. Refresh Token 유효성 검증 // 1. Refresh Token 유효성 검증
@@ -97,7 +98,7 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter {
response.setHeader("Authorization", "Bearer " + newAccessToken); response.setHeader("Authorization", "Bearer " + newAccessToken);
// Refresh Token 갱신 // Refresh Token 갱신
String newRefreshToken = jwtUtils.refreshTokens(username, role); String newRefreshToken = jwtUtils.createRefreshToken(username, role, httpUtils.getClientIp());
jwtUtils.setRefreshTokenCookie(response, newRefreshToken); jwtUtils.setRefreshTokenCookie(response, newRefreshToken);
// 인증 정보 설정 // 인증 정보 설정
@@ -114,6 +115,8 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter {
return; return;
} }
}
// 토큰이 없거나 모두 유효하지 않은 경우 // 토큰이 없거나 모두 유효하지 않은 경우
log.warn("유효한 JWT 토큰이 없습니다. URI: {}", request.getRequestURI()); log.warn("유효한 JWT 토큰이 없습니다. URI: {}", request.getRequestURI());
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.JWT_TOKEN_NULL)); sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.JWT_TOKEN_NULL));

View File

@@ -46,7 +46,7 @@ public class WebSecurity {
} }
private JwtTokenValidationFilter getJwtTokenValidationFilter() { private JwtTokenValidationFilter getJwtTokenValidationFilter() {
return new JwtTokenValidationFilter(jwtUtils, memberService, env, securityPathConfig); return new JwtTokenValidationFilter(jwtUtils, httpUtils, memberService, env, securityPathConfig);
} }

View File

@@ -37,7 +37,6 @@ public class JwtUtils {
// Token 생성 // Token 생성
public String generateToken(String username, String role, long expirationTime) { public String generateToken(String username, String role, long expirationTime) {
return Jwts.builder() return Jwts.builder()
.subject(username) .subject(username)
.claim("role", role) .claim("role", role)
@@ -47,26 +46,7 @@ public class JwtUtils {
.compact(); .compact();
} }
// Token 검증 // Token 생성(IP 정보 포함)
public Boolean validateAccessToken(String token) {
try {
return isTokenExpired(token);
} catch (io.jsonwebtoken.ExpiredJwtException e) {
log.debug("Access Token 만료: {}", e.getMessage());
return false;
} catch (Exception e) {
log.debug("Access Token 검증 실패: {}", e.getMessage());
return false;
}
}
// 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) { public String generateToken(String username, String role, String clientIp, long expirationTime) {
return Jwts.builder() return Jwts.builder()
.subject(username) .subject(username)
@@ -78,32 +58,42 @@ public class JwtUtils {
.compact(); .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 isValidTokenExpired(token);
} catch (io.jsonwebtoken.ExpiredJwtException e) {
log.debug("Access Token 만료: {}", e.getMessage());
return false;
} catch (Exception e) {
log.debug("Access Token 검증 실패: {}", e.getMessage());
return false;
}
}
// IP 정보 추출 // IP 정보 추출
public String extractClientIp(String token) { public String extractClientIp(String token) {
Claims claims = extractAllClaims(token); Claims claims = extractAllClaims(token);
return claims.get("ip", String.class); 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 유효성 검증 (토큰 일치, 만료 여부) // Refresh Token 유효성 검증 (토큰 일치, 만료 여부)
public Boolean isValidRefreshToken(String token) { public Boolean isValidRefreshToken(String token) {
try { try {
String savedToken = memberService.getRefreshToken(extractUsername(token)); String savedToken = memberService.getRefreshToken(extractUsername(token));
return savedToken.equals(token) && !isTokenExpired(token); return savedToken.equals(token) && isValidTokenExpired(token);
} catch (Exception e) { } catch (Exception e) {
log.debug("Refresh Token 검증 실패: {}", e.getMessage()); log.debug("Refresh Token 검증 실패: {}", e.getMessage());
return false; return false;
@@ -127,8 +117,9 @@ public class JwtUtils {
} }
} }
private boolean isTokenExpired(String token) { private boolean isValidTokenExpired(String token) {
return !extractAllClaims(token).getExpiration().before(new Date()); Date expiration = extractAllClaims(token).getExpiration();
return !expiration.before(new Date());
} }
public String extractUsername(String token) { public String extractUsername(String token) {
@@ -142,7 +133,6 @@ public class JwtUtils {
} }
public Claims extractAllClaims(String token) { public Claims extractAllClaims(String token) {
return Jwts.parser() return Jwts.parser()
.verifyWith(getSigningKey()) .verifyWith(getSigningKey())
.build() .build()
@@ -168,27 +158,6 @@ public class JwtUtils {
return null; 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 쿠키 설정 // Refresh Token 쿠키 설정
public void setRefreshTokenCookie(HttpServletResponse response, String refreshToken) { public void setRefreshTokenCookie(HttpServletResponse response, String refreshToken) {
Cookie refreshTokenCookie = new Cookie("RefreshToken", refreshToken); Cookie refreshTokenCookie = new Cookie("RefreshToken", refreshToken);