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
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
// 타임리프 페이지 인가 처리
String accessToken = resolveToken(request);
jwtUtils.validateToken(accessToken); // 토큰 검증
jwtUtils.isTokenBlacklisted(authHeader); // 🚨 블랙리스트 확인
jwtUtils.isTokenBlacklisted(accessToken); // 🚨 블랙리스트 확인

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

현재 isTokenBlacklisted 구현은 Redis의 KEYS 명령을 사용하여 운영 환경에서 심각한 성능 문제를 일으킬 수 있습니다. KEYS는 전체 키를 스캔하므로 사용을 피해야 합니다.

블랙리스트 확인을 위해 토큰에서 userId를 추출하고, blackList:{userId} 키를 직접 조회하여 토큰을 비교하는 것이 훨씬 효율적입니다. validateToken이 이미 호출되었으므로, 반환된 클레임에서 userId를 가져와 사용할 수 있습니다. 이 로직을 수정하여 성능 문제를 해결하는 것을 강력히 권장합니다.

} catch (Exception e) {
Gson gson = new Gson();
String json = "";
Expand Down Expand Up @@ -163,9 +163,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
private String resolveToken(HttpServletRequest request) {
// 1) Authorization 헤더: Bearer
String authHeader = request.getHeader(jwtHeader);
if (authHeader != null && authHeader.startsWith("Bearer ")) {
checkAuthorizationHeader(authHeader); // header 가 올바른 형식인지 체크
return JwtUtils.getTokenFromHeader(authHeader);
if (authHeader != null) {
if (authHeader.startsWith("Bearer ")){
checkAuthorizationHeader(authHeader); // header 가 올바른 형식인지 체크
return JwtUtils.getTokenFromHeader(authHeader);
}
}
Comment on lines +166 to 171

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

가독성을 위해 중첩 if 문을 사용하는 대신, 논리 AND 연산자(&&)를 사용하여 하나의 if 문으로 결합하는 것이 더 좋습니다. 이전 코드가 더 간결하고 읽기 쉬웠습니다.

        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            checkAuthorizationHeader(authHeader);   // header 가 올바른 형식인지 체크
            return JwtUtils.getTokenFromHeader(authHeader);
        }

// 2) 쿠키: ACCESS_TOKEN
if (request.getCookies() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ public IsAvailable verifyUsernameOverlap(String username) {

@Override
public void logout(String accessToken) {
String accessTokenWithoutBearer = accessToken.split(" ")[1];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

accessToken.split(" ")[1]accessToken이 "Bearer "로 시작하지 않거나 공백을 포함하지 않는 경우 ArrayIndexOutOfBoundsException을 발생시킬 수 있습니다. 토큰을 추출하기 전에 형식을 확인하고 substring을 사용하는 것이 더 안전합니다.

Suggested change
String accessTokenWithoutBearer = accessToken.split(" ")[1];
if (accessToken == null || !accessToken.startsWith("Bearer ")) {
throw new TtattaBackend.ttatta.jwt.exception.CustomJwtException("올바르지 않은 토큰 형식입니다.");
}
String accessTokenWithoutBearer = accessToken.substring("Bearer ".length());

// 로그아웃시킬 회원의 refresh token redis에서 삭제
Long userId = SecurityUtil.getCurrentUserId();
String key = "users:" + userId.toString();
Expand All @@ -266,7 +267,7 @@ public void logout(String accessToken) {
// 로그아웃시킬 회원의 access token redis의 블랙리스트로 저장
key = "blackList:" + userId.toString();
long tokenRemainTimeSecond = jwtUtils.tokenRemainTimeSecond(accessToken);
redisTemplate.opsForValue().set(key, accessToken, tokenRemainTimeSecond, TimeUnit.SECONDS);
redisTemplate.opsForValue().set(key, accessTokenWithoutBearer, tokenRemainTimeSecond, TimeUnit.SECONDS);

}

Expand Down