Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#13 [FEAT] 6주차 실습과제 #14

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

#13 [FEAT] 6주차 실습과제 #14

wants to merge 4 commits into from

Conversation

bbabbi
Copy link
Contributor

@bbabbi bbabbi commented Jun 2, 2024

🍀 이 주의 과제 🍀

✅ 이슈번호

close #13

✨ To reviewers

  • 로그인을 진행할 때 AccessToken과 Refresh Token을 함께 반환

@Transactional
public UserJoinResponse createMember(
MemberCreateDto memberCreate
) {
Member member = memberRepository.save(
Member.create(memberCreate.name(), memberCreate.part(), memberCreate.age())
);
Long memberId = member.getId();
// issue a token
String accessToken = jwtTokenProvider.issueAccessToken(
UserAuthentication.createUserAuthentication(memberId)
);
String refreshToken = jwtTokenProvider.issueRefreshToken(
UserAuthentication.createUserAuthentication(memberId)
);
// save RefreshToken in redis
tokenService.saveRefreshToken(memberId, refreshToken);
return UserJoinResponse.of(accessToken, refreshToken,memberId.toString());
}

Auth로 따로 분리하지 않고, 기존 세미나에서 진행한 코드에서 refreshToken을 발급하는 부분만 추가했습니다.

private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 60 * 1000L; // 60분으로 변경
private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 24 * 60 * 60 * 1000L * 14;
@Value("${jwt.secret}")
private String JWT_SECRET;
public String issueAccessToken(final Authentication authentication) {
return generateToken(authentication, ACCESS_TOKEN_EXPIRATION_TIME);
}
public String issueRefreshToken(final Authentication authentication) {
return generateToken(authentication, REFRESH_TOKEN_EXPIRATION_TIME);
}

JwtTokenProvider.java에서는 AccessToken의 기간을 RefreshToken보다 짧게 수정하고, issueRefreshToken을 추가했습니다.

public record UserJoinResponse(
String accessToken,
String refreshToken,
String userId
) {
public static UserJoinResponse of(
String accessToken,
String refreshToken,
String userId
) {
return new UserJoinResponse(accessToken, refreshToken, userId);
}
}

이에 따라 response에도 refreshToken에 대한 부분을 추가했습니다.

  • Redis를 활용해 Refresh Token으로 Access Token을 재발급

Redis에 대한 코드는 세미나 자료에서 다룬 것 이후로 TokenService.java와 RedisConfig.java를 추가했습니다.

스크린샷 2024-06-03 오후 4 15 55

public class TokenService {
private final TokenRepository tokenRepository;
private final JwtTokenProvider jwtTokenProvider;
@Transactional
public void saveRefreshToken(
final Long userId,
final String refreshToken
) {
tokenRepository.save(
Token.of(
userId,
refreshToken
)
);
}
public Long findIdByRefreshToken(
final String refreshToken
) {
Token token = tokenRepository.findByRefreshToken(refreshToken)
.orElseThrow(
() -> new NotFoundException(ErrorMessage.REFRESH_TOKEN_NOT_FOUND)
);
return token.getId();
}
public String generateAccessToken(Long userId) {
return jwtTokenProvider.issueAccessToken(
UserAuthentication.createUserAuthentication(userId)
);
}
}

TokenService.java는 요구조건이었던 Redis에 Refresh Token을 저장하는 메서드인 saveRefreshToken, RefreshToken에서 Id를 찾는 메서드인 findIdByRefreshToken, AccessToken을 생성하는 findIdByRefreshToken로 구성되어있습니다.

@PostMapping("/refresh")
public ResponseEntity<TokenResponse> login(
@RequestHeader("refreshToken") String refreshToken
) {
Long userId = tokenService.findIdByRefreshToken(refreshToken); // Refresh Token으로부터 userId 추출
String accessToken = tokenService.generateAccessToken(userId); // 새로운 Access Token 발급
return ResponseEntity.ok(new TokenResponse(accessToken));
}

위에서 작성된 findIdByRefreshToken을 통해 Header를 통해 전달받은 RefreshToken에서 Id를 찾고, 이에 대한 새로운 accessToken을 발급하도록 구현했습니다.

그리고 이렇게 발급된 accessToken을

public record TokenResponse (
String accessToken
) {
public static TokenResponse of(final String accessToken) {
return new TokenResponse(accessToken);
}
}

반환하도록 TokenResponse를 작성했습니다.

📌 Test

  • 로그인을 진행할 때 AccessToken과 Refresh Token을 함께 반환
스크린샷 2024-06-03 오후 4 12 07
  • Redis를 활용해 Refresh Token으로 Access Token을 재발급
스크린샷 2024-06-03 오후 4 06 42

❓ 질문있어요

private static final String[] AUTH_WHITE_LIST = {"/api/v1/member", "/api/v1/member/refresh"};

accessToken + refreshToken을 발급하는 건 잘 됐는데, refreshToken으로 다시 accessToken을 발급받는 부분에서 계속 401, 404에러가 뜨다가 화이트리스트에 해당 경로를 넣어주니까 잘 출력이 되었습니다. 이 부분을 맞게 해결한건지, 아니라면 원래는 어떻게 해결해야하는건지 궁금합니다

@bbabbi bbabbi added the 🍀 SERVER 과제 assignments of SOPT 34th SERVER seminar label Jun 2, 2024
@bbabbi bbabbi changed the title #13 [ADD] : add files of week 6 seminar #13 [FEAT] 6주차 실습과제 Jun 2, 2024
@jinkonu
Copy link

jinkonu commented Jun 4, 2024

코드 잘 읽었습니다~~~
건드릴 부분이 딱히 없어서 코멘트만 달아놓겠습니다~~~

질문 주신 거에 제가 이해한 대로 답변을 드리자면,
사용자가 토큰을 재발급받는 유즈케이스를 생각해보시면 될 것 같아요.
만약 사용자가 {로그아웃한 상태 + 액세스 토큰이 만료되어 로그인이 불가한 상태}라면 사용자를 인증하기 전에 API를 통해 액세스 토큰을 다시 받을 수 있도록 해야 할 것 같으니 화이트리스트에 넣어도 될 것 같네요!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🍀 SERVER 과제 assignments of SOPT 34th SERVER seminar
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEAT] 6주차 과제
2 participants