Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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 @@ -116,11 +116,11 @@ public ResponseEntity<Void> quit(
public ResponseEntity<ReissueResponse> reissueToken(
Authentication authentication
) {
String token = authentication.getCredentials().toString();
if (token == null) {
String accessToken = authentication.getCredentials().toString();
if (accessToken == null) {
Copy link
Copy Markdown
Contributor

@Gyuhyeok99 Gyuhyeok99 Apr 30, 2025

Choose a reason for hiding this comment

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

궁금한 점이 생겼습니다..!
지금 만료된 엑세스 토큰으로 리프레시토큰을 찾아와서 새 엑세스토큰을 발급해주는 거로 이해를 했습니다!

  1. 이러면 클라이언트에서 리프레시 토큰을 보관할 이유가 없어진 거 같은데 맞나요?
  2. 그래서 저는 클라이언트에서 만료된 엑세스 토큰 에러를 받으면 리프레시 토큰을 서버로 전송해서 그게 맞는지 검증하고 엑세스 토큰을 발급해주는 게 맞다고 생각했는데 혹시 이거 관해서 얘기한 게 있었나요? 제가 놓친 거 같아요 🥲
    (지금 방식이면 누군가 만료된 토큰을 탈취해도 계속 엑세스 토큰을 발급받는 거 아닌가란 생각이 들어서요!)

Copy link
Copy Markdown
Member

@wibaek wibaek Apr 30, 2025

Choose a reason for hiding this comment

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

어... 맞습니다 저는 저 accessToken이라는 이름으로 되있는게 refreshToken이라 생각했네요.
reissue API로 전송하는건 refresh token인걸로 알고 있어서요.

저도 전체 코드 다시 한번 읽어보고 말씀드리겠습니다

Copy link
Copy Markdown
Collaborator Author

@nayonsoso nayonsoso Apr 30, 2025

Choose a reason for hiding this comment

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

아하 그렇네요...! 제가 뭐랑 헷갈린걸까요... 😭😭

refreshToken을 POST 요청으로 받아,
Redis에 존재하는지 보고 존재한다면 새로운 accessToken 을 발급하도록 수정하면 될까요?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

이 방식이나 헤더로 받는 방식 두 개가 있을 거 같은데 전 둘 다 좋은 거 같아요!

throw new CustomException(ErrorCode.AUTHENTICATION_FAILED, "토큰이 없습니다.");
}
ReissueResponse reissueResponse = authService.reissue(token);
ReissueResponse reissueResponse = authService.reissue(accessToken);
return ResponseEntity.ok(reissueResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.example.solidconnection.auth.dto.ReissueResponse;
import com.example.solidconnection.config.security.JwtProperties;
import com.example.solidconnection.custom.exception.CustomException;
import com.example.solidconnection.siteuser.domain.SiteUser;
import lombok.RequiredArgsConstructor;
Expand All @@ -12,12 +13,14 @@
import java.util.Optional;

import static com.example.solidconnection.custom.exception.ErrorCode.REFRESH_TOKEN_EXPIRED;
import static com.example.solidconnection.util.JwtUtils.parseSubject;

@RequiredArgsConstructor
@Service
public class AuthService {

private final AuthTokenProvider authTokenProvider;
private final JwtProperties jwtProperties;

/*
* 로그아웃 한다.
Expand All @@ -43,14 +46,15 @@ public void quit(SiteUser siteUser) {
* - 리프레시 토큰이 만료되었거나, 존재하지 않는다면 예외 응답을 반환한다.
* - 리프레시 토큰이 존재한다면, 액세스 토큰을 재발급한다.
* */
public ReissueResponse reissue(String subject) {
public ReissueResponse reissue(String accessToken) {
// 리프레시 토큰 만료 확인
String subject = parseSubject(accessToken, jwtProperties.secret());
Optional<String> optionalRefreshToken = authTokenProvider.findRefreshToken(subject);
if (optionalRefreshToken.isEmpty()) {
throw new CustomException(REFRESH_TOKEN_EXPIRED);
}
// 액세스 토큰 재발급
String newAccessToken = authTokenProvider.generateAccessToken(subject);
Comment on lines -46 to -53
Copy link
Copy Markdown
Collaborator Author

@nayonsoso nayonsoso Apr 30, 2025

Choose a reason for hiding this comment

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

컨트롤러에서 accessToken을 authService.reissue() 함수의 인자로 넘겨줬는데,
reissue 서비스 코드에서는 String subject 라는 파라미터 이름을 사용해서,
마치 컨트롤러에서 subject를 바로 넘겨받은 것으로 해석해서 로직을 짰었습니다 🥲

변경된 코드에서는 파라미터 이름을 accessToken 으로 바꿔 오해를 방지하고,
토큰에서 subject를 추출하여 이후 로직을 수행하도록 했습니다.

String newAccessToken = authTokenProvider.generateAccessToken(accessToken);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

엑세스 토큰 재발급은
authTokenProvider.generateAccessToken(accessToken);
가 아니라
authTokenProvider.generateAccessToken(subject);일 거 같은데 맞나요?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

ㅋㅎㅎㅎ... 이런 실수 방지하려면 Token 클래스가 정말 필요하겠네요..

return new ReissueResponse(newAccessToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

import java.util.Optional;

import static com.example.solidconnection.util.JwtUtils.parseSubjectIgnoringExpiration;

@Component
public class AuthTokenProvider extends TokenProvider {

Expand All @@ -18,7 +16,7 @@ public AuthTokenProvider(JwtProperties jwtProperties, RedisTemplate<String, Stri
}

public String generateAccessToken(SiteUser siteUser) {
String subject = siteUser.getId().toString();
String subject = getSubject(siteUser);
return generateToken(subject, TokenType.ACCESS);
}

Expand All @@ -27,7 +25,7 @@ public String generateAccessToken(String subject) {
}

public String generateAndSaveRefreshToken(SiteUser siteUser) {
String subject = siteUser.getId().toString();
String subject = getSubject(siteUser);
String refreshToken = generateToken(subject, TokenType.REFRESH);
return saveToken(refreshToken, TokenType.REFRESH);
}
Expand All @@ -47,7 +45,7 @@ public Optional<String> findBlackListToken(String subject) {
return Optional.ofNullable(redisTemplate.opsForValue().get(blackListTokenKey));
}

public String getEmail(String token) {
return parseSubjectIgnoringExpiration(token, jwtProperties.secret());
private String getSubject(SiteUser siteUser) {
return siteUser.getId().toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ public static String parseTokenFromRequest(HttpServletRequest request) {
return token.substring(TOKEN_PREFIX.length());
}

public static String parseSubjectIgnoringExpiration(String token, String secretKey) {
public static String parseSubject(String token, String secretKey) {
try {
return parseClaims(token, secretKey).getSubject();
} catch (ExpiredJwtException e) {
return e.getClaims().getSubject();
} catch (Exception e) {
throw new CustomException(INVALID_TOKEN);
}
}

public static String parseSubject(String token, String secretKey) {
public static String parseSubjectIgnoringExpiration(String token, String secretKey) {
try {
return parseClaims(token, secretKey).getSubject();
} catch (ExpiredJwtException e) {
return e.getClaims().getSubject();
} catch (Exception e) {
throw new CustomException(INVALID_TOKEN);
}
Expand Down
Loading