Skip to content

Commit 1b76d9c

Browse files
committed
[Fix]: 401 에러 처리 개선 및 유효한 AT,RT에 의한 토큰 재발급 기능 구현 #29
1 parent f83a904 commit 1b76d9c

File tree

4 files changed

+65
-26
lines changed

4 files changed

+65
-26
lines changed

src/main/java/org/dfbf/soundlink/domain/user/service/UserService.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ public class UserService {
4747
private final RedisService redisService;
4848

4949
private final JwtProvider jwtProvider;
50-
private final TokenProperties tokenProperties;
5150

52-
private RedisTemplate<String, String> redisTemplate;
5351
private final TokenService tokenService;
5452

5553
private final String domain = "";
@@ -222,7 +220,6 @@ public ResponseResult login(LoginReqDto loginReqDto, HttpServletResponse respons
222220

223221
return new ResponseResult(responseBody);
224222
} catch (Exception e) {
225-
System.out.println("[ERROR] " + e.getMessage());
226223
return new ResponseResult(ErrorCode. INTERNAL_SERVER_ERROR);
227224
}
228225
}
@@ -283,7 +280,7 @@ public ResponseResult getProfile(String tag) {
283280
}
284281
}
285282

286-
// 토큰 재발급
283+
// 토큰 재발급(AT O, RT O => AT,RT 재발급)
287284
public ResponseResult reissueToken(HttpServletRequest request, HttpServletResponse response) {
288285
String accessToken = jwtProvider.resolveAccessToken(request);
289286
String refreshToken = jwtProvider.resolveRefreshToken(request);
@@ -307,10 +304,10 @@ public ResponseResult reissueToken(HttpServletRequest request, HttpServletRespon
307304
return new ResponseResult(ErrorCode.TOKEN_INVALID, "RT가 존재하지 않거나 만료되었습니다.");
308305
}
309306

310-
// AccessToken 유효성 확인
311-
if (jwtProvider.validateToken(accessToken)) {
312-
return new ResponseResult(ErrorCode.TOKEN_NOT_EXPIRED); // 유효한 액세스 토큰: 재발급 x
313-
}
307+
// // AccessToken 유효성 확인
308+
// if (jwtProvider.validateToken(accessToken)) {
309+
// return new ResponseResult(ErrorCode.TOKEN_NOT_EXPIRED); // 유효한 액세스 토큰: 재발급 x
310+
// }
314311

315312
// RefreshToken 유효성 확인
316313
if (jwtProvider.validateToken(refreshToken)) {
@@ -324,8 +321,8 @@ public ResponseResult reissueToken(HttpServletRequest request, HttpServletRespon
324321
String newAccessToken = jwtProvider.createAccessToken(userId);
325322
String newRefreshToken = jwtProvider.createRefreshToken(userId);
326323

327-
System.out.println("New AccessToken: " + newAccessToken);
328-
System.out.println("New RefreshToken: " + newRefreshToken);
324+
// System.out.println("New AccessToken: " + newAccessToken);
325+
// System.out.println("New RefreshToken: " + newRefreshToken);
329326

330327
//레디스에 새로운 리프레시 토큰 업데이트!
331328
tokenService.updateRefreshToken(userId, newRefreshToken);

src/main/java/org/dfbf/soundlink/global/auth/JwtAuthenticationFilter.java

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,54 @@
1010
import lombok.RequiredArgsConstructor;
1111
import org.dfbf.soundlink.global.exception.ErrorCode;
1212
import org.dfbf.soundlink.global.exception.ResponseResult;
13-
import org.slf4j.Logger;
14-
import org.slf4j.LoggerFactory;
1513
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1614
import org.springframework.security.core.context.SecurityContextHolder;
17-
import org.springframework.stereotype.Component;
1815
import org.springframework.web.filter.OncePerRequestFilter;
1916

2017
import java.io.IOException;
2118

2219
@RequiredArgsConstructor
2320
public class JwtAuthenticationFilter extends OncePerRequestFilter {
2421
private final JwtProvider jwtProvider;
22+
private final ObjectMapper objectMapper = new ObjectMapper();
2523

2624
@Override
2725
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
28-
String accessToken = jwtProvider.resolveAccessToken(request); //1.Access Token 추출
26+
String accessToken = null;
27+
try {
28+
accessToken = jwtProvider.resolveAccessToken(request); // 1. Access Token 추출
2929

30-
if(accessToken !=null && jwtProvider.validateToken(accessToken)) { //2.유효성 검사
31-
this.setAuthentication(accessToken); //3.유저정보 저장
30+
if (accessToken != null) {
31+
// 2. 유효성 검사
32+
if (!jwtProvider.validateToken(accessToken)) {
33+
throw new JwtException("Invalid token");
34+
}
35+
36+
// 3. 만료 여부 검사
37+
if (jwtProvider.isTokenExpired(accessToken)) {
38+
throw new ExpiredJwtException(null, null, "Token expired");
39+
}
40+
41+
// 4. 유저정보 저장
42+
this.setAuthentication(accessToken);
43+
}
44+
filterChain.doFilter(request, response); // 필터 체인 진행(전달)
45+
46+
} catch (ExpiredJwtException ex) {
47+
System.out.println("[ERROR] Expired Token: " + accessToken); // 만료된 토큰 로그 출력
48+
handleException(response, ErrorCode.TOKEN_EXPIRED);
49+
50+
} catch (JwtException ex) {
51+
System.out.println("[ERROR] Invalid Token: " + accessToken); // 유효하지 않은 토큰 로그 출력
52+
handleException(response, ErrorCode.TOKEN_INVALID);
53+
54+
} catch (Exception ex) {
55+
handleException(response, ErrorCode.INTERNAL_SERVER_ERROR);
3256
}
33-
filterChain.doFilter(request, response); //필터 체인 진행(전달)
3457
}
3558

59+
60+
3661
//유저정보 저장
3762
public void setAuthentication(String token) {
3863
Long userId = jwtProvider.getUserId(token); //userId 추출
@@ -50,5 +75,14 @@ public void setAuthentication(String token) {
5075
// 인증정보 설정
5176
SecurityContextHolder.getContext().setAuthentication(authentication);
5277
}
78+
// 예외 발생 시 JSON 응답을 반환하는 메서드
79+
private void handleException(HttpServletResponse response, ErrorCode errorCode) throws IOException {
80+
ResponseResult responseResult = new ResponseResult(errorCode);
81+
82+
response.setStatus(errorCode.getStatus().value());
83+
response.setContentType("application/json");
84+
response.setCharacterEncoding("UTF-8");
85+
response.getWriter().write(objectMapper.writeValueAsString(responseResult));
86+
}
5387

5488
}

src/main/java/org/dfbf/soundlink/global/auth/JwtProvider.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package org.dfbf.soundlink.global.auth;
22

3-
import ch.qos.logback.core.subst.Token;
43
import io.jsonwebtoken.security.Keys;
5-
import io.jsonwebtoken.security.SignatureException;
64
import jakarta.servlet.http.Cookie;
75
import jakarta.servlet.http.HttpServletRequest;
8-
import org.dfbf.soundlink.domain.user.exception.ExpiredTokenException;
9-
import org.dfbf.soundlink.global.exception.ErrorCode;
10-
import org.dfbf.soundlink.global.exception.ResponseResult;
116
import org.springframework.beans.factory.annotation.Autowired;
127
import org.springframework.beans.factory.annotation.Value;
138
import org.springframework.data.redis.core.RedisTemplate;
@@ -28,7 +23,7 @@ public class JwtProvider {
2823
@Value("${REFRESH_TOKEN_EXPIRATION_TIME}")
2924
private long REFRESH_EXPIRATION_TIME;
3025

31-
//시크릿 키 자동 생성
26+
//시크릿 키
3227
private final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
3328

3429
@Autowired
@@ -80,6 +75,19 @@ public boolean validateToken(String token){
8075
}
8176
}
8277

78+
public boolean isTokenExpired(String token) {
79+
try {
80+
Jwts.parserBuilder()
81+
.setSigningKey(SECRET_KEY)
82+
.build()
83+
.parseClaimsJws(token); // 만료된 토큰을 처리하려면 ExpiredJwtException이 발생함
84+
return false; // 만료되지 않으면 false
85+
} catch (ExpiredJwtException ex) {
86+
return true; // 만료된 경우 true
87+
} catch (Exception ex) {
88+
return false; // 다른 예외는 false
89+
}
90+
}
8391

8492

8593
//액세스토큰 추출

src/main/java/org/dfbf/soundlink/global/exception/ErrorCode.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ public enum ErrorCode {
4646

4747
// Auth
4848
TOKEN_NOT_EXPIRED(HttpStatus.OK, "토큰 정상"),
49-
TOKEN_EXPIRED(HttpStatus.BAD_REQUEST, "토큰 만료됨"),
50-
TOKEN_TAMPERED(HttpStatus.BAD_REQUEST, "토큰 변조됨"),
51-
TOKEN_INVALID(HttpStatus.BAD_REQUEST,"유효하지 않은 토큰"),
49+
TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "토큰 만료됨"),
50+
TOKEN_TAMPERED(HttpStatus.UNAUTHORIZED, "토큰 변조됨"),
51+
TOKEN_INVALID(HttpStatus.UNAUTHORIZED,"유효하지 않은 토큰"),
5252

5353
// 카카오페이 결제 에러
5454
KAKAOPAY_READY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오페이 결제 준비 에러"),

0 commit comments

Comments
 (0)