Post

Access token, Refresh token

✅ Access token, Refresh token with body ## ✅ Access token, Refresh token with body ### ✔️ Reference - easy to follow ver - <https://www.bezkoder.com/spring-boot-refresh-token-jwt/> - refresh token and logout - <https://medium.com/@max.difranco/user-registration-and-jwt-authentication-with-spring-boot-3-part-3-refresh-token-logout-ea0704f1b436> ### ☑️ RefreshToken Entity ```java @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class RefreshToken { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="token_id") private int tokenId; @Column(name= "token") private String token; @Column(name= "expiry_date") private Instant expiryDate; @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user; @Builder public RefreshToken(int tokenId, String token, Instant expiryDate, User user) { this.tokenId = tokenId; this.token = token; this.expiryDate = expiryDate; this.user = user; } public static RefreshToken toEntity(String token, Instant expiryDate, User user){ return RefreshToken.builder() .token(token) .expiryDate(expiryDate) .user(user) .build(); } public void updateToken(String token, Instant expiryDate){ this.token = token; this.expiryDate = expiryDate; } } ``` ### ☑️ TokenRefreshRequest DTO ```java public record TokenRefreshRequest ( String refreshToken ){ } ``` ### ☑️ LoginResponse DTO ```java @Builder public record LoginResponse ( String email, String accessToken, String refreshToken ){ } ``` ### ☑️ JwtResponse DTO ```java public record JwtResponse ( String accessToken, String refreshToken ){ public JwtResponse(String accessToken, String refreshToken) { this.accessToken = accessToken; this.refreshToken = refreshToken; } } ``` ### ☑️ Auth Controller ```java @Transactional @Operation(summary = "login", description = "Login API with email and password") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Login success") }) @PostMapping(value = "/login") public Response login( @Valid @RequestBody LoginRequest loginRequest, HttpServletResponse httpServletResponse){ String accessToken = userService.login(loginRequest); RefreshToken refreshToken = refreshTokenService.createRefreshToken(loginRequest.email()); httpServletResponse.addHeader("Access-Control-Expose-Headers", "token"); httpServletResponse.setHeader("token", accessToken); return Response.ok(new LoginResponse(loginRequest.email(), accessToken, refreshToken.getToken())); } @PostMapping(value = "/refresh") public Response refreshToken( @Valid @RequestBody TokenRefreshRequest request ){ return Response.ok(refreshTokenService.refreshToken(request)); } ``` ### ☑️ JwtTokenProvider ```java @Component @RequiredArgsConstructor public class JwtTokenProvider { private static final long TOKEN_VALID_MILLI_SECONDS =1000L*60*60*24; //24h @Value("${jwtpassword.source}") private String secretKeySource; private String secretKey; @PostConstruct public void setUp(){ secretKey = Base64.getEncoder() .encodeToString(secretKeySource.getBytes()); } private final UserDetailsService userDetailsService; public String createToken(String email){ Claims claims = Jwts.claims().setSubject(email); Date now = new Date(); return Jwts.builder() .setClaims(claims) .setIssuedAt(now) .setExpiration(new Date(now.getTime()+ TOKEN_VALID_MILLI_SECONDS)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); } public String resolveToken(HttpServletRequest request){ String token = request.getHeader("token"); return token; } ``` ### ☑️ RefreshTokenService ```java public interface RefreshTokenService { RefreshToken createRefreshToken(String email); RefreshToken verifyExpiration(RefreshToken token); Optional findByToken(String requestRefreshToken); JwtResponse refreshToken(TokenRefreshRequest request); } ``` ### ☑️ RefreshTokenService ```java public interface RefreshTokenService { RefreshToken createRefreshToken(String email); RefreshToken verifyExpiration(RefreshToken token); Optional findByToken(String requestRefreshToken); String refreshToken(TokenRefreshRequest request); } ``` ### ☑️ RefreshTokenServiceImpl ```java @Service @RequiredArgsConstructor public class RefreshTokenServiceImpl implements RefreshTokenService { private final UserRepository userRepository; private final RefreshTokenRepository refreshTokenRepository; private final JwtTokenProvider jwtTokenProvider; private static final long REFRESH_TOKEN_VALID_MILLI_SECONDS =1000L*60*60*12; //12h public Optional findByToken(String token) { return refreshTokenRepository.findByToken(token); } @Override public JwtResponse refreshToken(TokenRefreshRequest request) { String requestRefreshToken = request.refreshToken(); RefreshToken verifiedToken = findByToken(requestRefreshToken) .map(token -> verifyExpiration(token)) .orElseThrow(()-> new TokenRefreshException("Refresh token is not in database.")); String accessToken = jwtTokenProvider.createToken(verifiedToken.getUser().getEmail()); return new JwtResponse(accessToken, requestRefreshToken); } @Transactional @Override public RefreshToken createRefreshToken(String email){ User user = userRepository.findByEmail(email) .orElseThrow(()-> new UserNotFoundException(email)); String token = UUID.randomUUID().toString(); Instant instant = Instant.now().plusMillis(REFRESH_TOKEN_VALID_MILLI_SECONDS); if(refreshTokenRepository.existsByUser(user)){ RefreshToken refreshToken = refreshTokenRepository.findByUser(user); refreshToken.updateToken(token, instant); RefreshToken newRefreshToken = refreshTokenRepository.save(refreshToken); return newRefreshToken; }else { RefreshToken refreshToken = RefreshToken.toEntity(token, instant, user); refreshTokenRepository.save(refreshToken); return refreshToken; } } @Override public RefreshToken verifyExpiration(RefreshToken token){ if(token.getExpiryDate().compareTo(Instant.now()) < 0) { refreshTokenRepository.delete(token); throw new TokenRefreshException("Refresh token expired. Please make a new login request. Refresh token expired at: " + token.getExpiryDate()); } return token; } } ``` </details> ### ☑️ ```java ``` ### ☑️ ```java ``` ## ✅ Cookie reference <https://be-student.tistory.com/72#SameSite%EC%99%80%20Secure%20%EC%98%B5%EC%85%98-1> ## ✅ Refresh token RTR - <https://hayden-igm.tistory.com/58> ## ✅ ## ✅ ## ✅ ## ✅ ## ✅ ## ✅ ## ✅ ## ✅
This post is licensed under CC BY 4.0 by the author.