Skip to content

Commit

Permalink
Merge pull request #87 from KNU-HAEDAL/fix/jwt
Browse files Browse the repository at this point in the history
Fix/jwt
  • Loading branch information
bayy1216 authored Sep 8, 2024
2 parents 56be2c7 + acc1234 commit 6e82b13
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import org.haedal.zzansuni.user.domain.UserModel;
import org.haedal.zzansuni.global.jwt.JwtToken;
import org.springframework.data.util.Pair;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
Expand All @@ -19,9 +18,7 @@
@RequiredArgsConstructor
@RestController
public class AuthController {

private final AuthService authService;
private final BCryptPasswordEncoder passwordEncoder;

@Operation(summary = "oauth2 로그인", description = "oauth2 code를 이용하여 로그인한다.")
@PostMapping("/api/auth/oauth2")
Expand Down Expand Up @@ -51,7 +48,7 @@ public ApiResponse<AuthRes.LoginResponse> login(
return ApiResponse.success(response);
}

@Operation(summary = "액세스 토큰 재발급", description = "리프레시 토큰을 이용하여 액세스 토큰을 재발급한다.")
@Operation(summary = "JWT 재발급", description = "리프레시 토큰을 이용하여 JWT를 재발급한다. 발급에 사용된 리프레시 토큰은 만료된다.")
@PostMapping("/api/auth/refresh")
public ApiResponse<AuthRes.JwtResponse> refresh(
@RequestHeader("Authorization") String authorization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public JwtToken reissueToken(String rawToken) {
}

/**
* 중복 uuid 저장이 발생하는 경우를 대비하여 10번까지 시도한다.
* 중복 uuid 저장이 발생하는 경우를 대비하여 5번까지 시도한다.
* 유저 생성과 리프래시토큰 저장을 한 트랜잭션에서 처리하게 된다면
* 모든 처리가 롤백되어야 한다. <br>
* 데이터베이스에서 발생한 에러는 트랜잭션 상태에 영향을 미쳐 예외가 발생한 후의 추가 처리에 실패한다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;

@Slf4j
@Component
Expand All @@ -32,7 +33,9 @@ public JwtToken invoke(User user) {
JwtUser jwtUser = JwtUser.of(user.getId(), user.getRole());
String uuid = uuidHolder.random();
JwtToken jwtToken = jwtUtils.generateToken(jwtUser, uuid);
RefreshToken refreshToken = RefreshToken.create(uuid, user, jwtToken.getRefreshTokenExpireAt());

LocalDateTime expiredAt = jwtToken.getRefreshTokenExpireAt().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
RefreshToken refreshToken = RefreshToken.create(uuid, user, expiredAt);
refreshTokenStore.flushSave(refreshToken);
return jwtToken;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;
import java.util.Date;

@Getter
@Builder
public class JwtToken {
private String accessToken;
private String refreshToken;
private LocalDateTime refreshTokenExpireAt;
private Date refreshTokenExpireAt;

/**
* 유효한 토큰을 나타내는 VO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.time.LocalDateTime;
import java.util.Date;

@Component
Expand All @@ -32,9 +31,12 @@ public class JwtUtils implements InitializingBean {


public JwtToken generateToken(JwtUser jwtUser, String refreshUuid) {
String accessToken = generateAccessToken(jwtUser);
String refreshToken = generateRefreshToken(jwtUser, refreshUuid);
LocalDateTime refreshTokenExpireAt = LocalDateTime.now().plusSeconds(REFRESH_TOKEN_EXPIRE_TIME/1000);
long systemTimeMillis = System.currentTimeMillis();
Date accessTokenExpireAt = new Date(systemTimeMillis + ACCESS_TOKEN_EXPIRE_TIME);
Date refreshTokenExpireAt = new Date(systemTimeMillis + REFRESH_TOKEN_EXPIRE_TIME);

String accessToken = generateAccessToken(jwtUser, accessTokenExpireAt);
String refreshToken = generateRefreshToken(jwtUser, refreshUuid, refreshTokenExpireAt);
return JwtToken.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
Expand All @@ -46,10 +48,20 @@ public JwtToken generateToken(JwtUser jwtUser, String refreshUuid) {
* Jwt가 유효한지 검사하는 메서드.
* 만료시간, 토큰의 유효성을 검사한다.
*/
public boolean validateToken(String rawToken) {
public boolean validateAccessToken(String rawToken) {
try {
Claims claims = extractClaims(rawToken);
return !claims.getExpiration().before(new Date());
Boolean isAccessToken = claims.get(IS_ACCESS_TOKEN, Boolean.class);
if(!isAccessToken) {
return false;
}
if(!claims.getIssuer().equals(ISSUER)) {
return false;
}
if(claims.getExpiration().before(new Date())) {
return false;
}
return true;
} catch (Exception e) {//JwtException, ExpiredJwtException, NullPointerException
return false;
}
Expand All @@ -60,7 +72,7 @@ public UserIdAndUuid validateAndGetUserIdAndUuid(String rawToken) {
try {
Claims claims = extractClaims(rawToken);
Boolean isAccessToken = claims.get(IS_ACCESS_TOKEN, Boolean.class);
if (isAccessToken == null || isAccessToken) {
if (isAccessToken) {
throw new UnauthorizedException("RefreshToken이 유효하지 않습니다.");
}
Long userId = Long.parseLong(claims.getSubject());
Expand Down Expand Up @@ -88,16 +100,16 @@ public JwtUser getJwtUser(JwtToken.ValidToken token) {

private JwtUser claimsToJwtUser(Claims claims) {
Role userRole = Role.valueOf(claims.get(ROLE, String.class));
return JwtUser.of(Long.parseLong(claims.getSubject()), userRole);
Long userId = Long.parseLong(claims.getSubject());
return JwtUser.of(userId, userRole);
}


/**
* Jwt 토큰생성
* 엑세스토큰 여부, 유저 id, role을 저장한다.
*/
private String generateAccessToken(JwtUser jwtUser) {
Date expireDate = new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRE_TIME);
private String generateAccessToken(JwtUser jwtUser, Date expireDate) {
return Jwts.builder()
.signWith(secretKey)
.claim(ROLE, jwtUser.getRole().toString())
Expand All @@ -112,8 +124,7 @@ private String generateAccessToken(JwtUser jwtUser) {
* refreshToken 생성
* 엑세스토큰 여부, uuid, 유저 아이디를 저장한다.
*/
private String generateRefreshToken(JwtUser jwtUser, String refreshUuid) {
Date expireDate = new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRE_TIME);
private String generateRefreshToken(JwtUser jwtUser, String refreshUuid, Date expireDate) {
return Jwts.builder()
.signWith(secretKey)
.claim(IS_ACCESS_TOKEN, false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
String token = (String) authentication.getCredentials();

// 토큰을 검증하는 단계
if (!jwtUtils.validateToken(token)) {
if (!jwtUtils.validateAccessToken(token)) {
throw new AuthenticationServiceException("유효하지 않은 토큰입니다.");
}
JwtUser jwtUser = jwtUtils.getJwtUser(JwtToken.ValidToken.of(token));
Expand Down

0 comments on commit 6e82b13

Please sign in to comment.