먼저 yml 파일에 refresh 토큰 만료시간 세팅 해준다.
app:
jwtSecret: jwtsigntutorialasdfasdfasdfasdfasdf
jwtExpirationInMs: 604800000
# 여기 리프레시 한줄 추가
jwtRefreshExpirationInMs: 86400000
Request
TokenRefreshRequest.java 파일
public class TokenRefreshRequest {
@NotBlank
private String refreshToken;
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
}
Response
기존 작성해놓았던 JwtAuthenticationResponse.java
파일에
refreshToken 변수를 추가했다.
@Getter @Setter
public class JwtAuthenticationResponse {
private String accessToken;
private String refreshToken;
private String tokenType = "Bearer";
public JwtAuthenticationResponse(String accessToken, String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}
그리고 refreshToken을 받아올 클래스 작성
2. TokenRefreshResponse.java
@Getter @Setter
public class TokenRefreshResponse {
private String accessToken;
private String refreshToken;
private String tokenType = "Bearer";
public TokenRefreshResponse(String accessToken, String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}
기존 작성해놓았던 AuthController.java
파일에 refreshToken 로직을 추가해준다.
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RefreshTokenService refreshTokenService;
@Autowired
UserRepository userRepository;
@Autowired
RoleRepository roleRepository;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
JwtTokenProvider tokenProvider;
# 로그인 부분에 RefreshToken 추가
@PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getEmail(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
#RefreshToken 로직 추가
RefreshToken refreshToken = refreshTokenService.createRefreshToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt, refreshToken.getToken()));
}
# RefreshToken api 작성
@PostMapping("/refreshtoken")
public ResponseEntity<?> refreshtoken(@Valid @RequestBody TokenRefreshRequest tokenRefreshRequest) {
String requestRefreshToken = tokenRefreshRequest.getRefreshToken();
return refreshTokenService.findByToken(requestRefreshToken)
.map(refreshTokenService::verifyExpiration)
.map(RefreshToken::getUser)
.map(user -> {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
user.getEmail(), user.getPassword()
)
);
String token = tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new TokenRefreshResponse(token, requestRefreshToken));
})
.orElseThrow(() -> new TokenRefreshException(requestRefreshToken, "Refresh token is not in database!"));
}
}
아래에서
Refresh Token 모델을 작성하고,
Jwt Refresh Token Service 작성할것이다.
@Getter @Setter
@Entity(name = "refreshtoken")
public class RefreshToken {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToOne
@JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
@Column(nullable = false, unique = true)
private String token;
@Column(nullable = false)
private Instant expiryDate;
}
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
@Override
Optional<RefreshToken> findById(Long id);
Optional<RefreshToken> findByToken(String token);
int deleteByUser(User user);
}
@Service
public class RefreshTokenService {
@Value("${app.jwtRefreshExpirationInMs}")
private Long refreshTokenDurationMs;
@Autowired
private RefreshTokenRepository refreshTokenRepository;
@Autowired
private UserRepository userRepository;
public Optional<RefreshToken> findByToken(String token) {
return refreshTokenRepository.findByToken(token);
}
public RefreshToken createRefreshToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal)authentication.getPrincipal();
RefreshToken refreshToken = new RefreshToken();
refreshToken.setUser(userRepository.findById(userPrincipal.getId()).get());
refreshToken.setExpiryDate(Instant.now().plusMillis(refreshTokenDurationMs));
refreshToken.setToken(UUID.randomUUID().toString());
refreshToken = refreshTokenRepository.save(refreshToken);
return refreshToken;
}
public RefreshToken verifyExpiration(RefreshToken token) {
if (token.getExpiryDate().compareTo(Instant.now()) < 0) {
refreshTokenRepository.delete(token);
throw new TokenRefreshException(token.getToken(), "Refresh token was expired. Please make a new signin request");
}
return token;
}
@Transactional
public int deleteByUserId(Long userId) {
return refreshTokenRepository.deleteByUser(userRepository.findById(userId).get());
}
}
TokenRefreshException.java
@ResponseStatus(HttpStatus.FORBIDDEN)
@Getter
@Setter
public class TokenRefreshException extends RuntimeException {
private static final long serialVersionUID = 1L;
public TokenRefreshException(String token, String message) {
super(String.format("Failed for [%s]: %s", token, message));
}
}
controller에 refreshToken 로직을 추가하는 이유가 있을까요?