In previous post, we’ve known how to build Token based Authentication & Authorization with Spring Security & JWT. This tutorial will continue to make JWT Refresh Token in the Java Spring Boot Application. You can know how to expire the JWT, then renew the Access Token with Refresh Token.
Related Posts:
- Spring Boot, Spring Data JPA – Building Rest CRUD API example
- Spring Boot File upload example with Multipart File
- @RestControllerAdvice example in Spring Boot
- Spring Boot @ControllerAdvice & @ExceptionHandler example
- @DataJpaTest example for Spring Data Repository Unit Test
- Spring Boot JWT Authentication with Spring Security and MongoDB
- Documentation: Spring Boot Swagger 3 example
Deployment:
– Deploy Spring Boot App on AWS – Elastic Beanstalk
– Docker Compose: Spring Boot and MySQL example
The code in this post bases on previous article that you need to read first:
Spring Boot Token based Authentication with Spring Security & JWT
Contents
Overview of Spring Boot Refresh Token with JWT example
We already have a Spring Boot application in that:
- User can signup new account, or login with username & password.
- By User’s role (admin, moderator, user), we authorize the User to access resources
With APIs:
Methods | Urls | Actions |
---|---|---|
POST | /api/auth/signup | signup new account |
POST | /api/auth/signin | login an account |
GET | /api/test/all | retrieve public content |
GET | /api/test/user | access User’s content |
GET | /api/test/mod | access Moderator’s content |
GET | /api/test/admin | access Admin’s content |
For more details, please visit this post.
We’re gonna add Token Refresh to this Spring Boot – Spring Security Project.
The final result can be described with following requests/responses:
– Send /signin
request, return response with refreshToken
.
– Access resource successfully with accessToken
.
– When the accessToken
is expired, user cannot use it anymore.
– Send /refreshtoken
request, return response with new accessToken
.
– Access resource successfully with new accessToken
.
– Send an expired Refresh Token.
– Send an inexistent Refresh Token.
– Axios Client to check this: Axios Interceptors tutorial with Refresh Token example
– Using React Client:
– Or Vue Client:
– Angular Client:
Flow for Spring Boot Refresh Token with JWT
The diagram shows flow of how we implement Authentication process with Access Token and Refresh Token.
– A legal JWT
must be added to HTTP Authorization Header if Client accesses protected resources.
– A refreshToken
will be provided at the time user signs in.
If you want to use HttpOnly Cookie for JWT instead, kindly visit:
Spring Security Refresh Token with JWT
How to Expire JWT Token in Spring Boot
The Refresh Token has different value and expiration time to the Access Token.
Regularly we configure the expiration time of Refresh Token larger than Access Token’s.
Open application.properties for configuring App properties:
# Spring Datasource, Spring Data...
# App Properties
bezkoder.app.jwtSecret= bezKoderSecretKey
bezkoder.app.jwtExpirationMs= 3600000
bezkoder.app.jwtRefreshExpirationMs= 86400000
## For test
#bezkoder.app.jwtExpirationMs= 60000
#bezkoder.app.jwtRefreshExpirationMs= 120000
Update JwtUtils
class. Now it has 3 main functions:
- generate a
JWT
from username - get username from
JWT
- validate a
JWT
: JWT Access Token is expired withExpiredJwtException
security/jwt/JwtUtils.java
package com.bezkoder.spring.security.jwt.security.jwt;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.bezkoder.spring.security.jwt.security.services.UserDetailsImpl;
import io.jsonwebtoken.*;
@Component
public class JwtUtils {
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
@Value("${bezkoder.app.jwtSecret}")
private String jwtSecret;
@Value("${bezkoder.app.jwtExpirationMs}")
private int jwtExpirationMs;
public String generateJwtToken(UserDetailsImpl userPrincipal) {
return generateTokenFromUsername(userPrincipal.getUsername());
}
public String generateTokenFromUsername(String username) {
return Jwts.builder().setSubject(username).setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)).signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUserNameFromJwtToken(String token) {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
}
public boolean validateJwtToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
logger.error("Invalid JWT signature: {}", e.getMessage());
} catch (MalformedJwtException e) {
logger.error("Invalid JWT token: {}", e.getMessage());
} catch (ExpiredJwtException e) {
logger.error("JWT token is expired: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
logger.error("JWT token is unsupported: {}", e.getMessage());
} catch (IllegalArgumentException e) {
logger.error("JWT claims string is empty: {}", e.getMessage());
}
return false;
}
}
Refresh Token Request and Response
Update the payloads for our RestAPIs:
– Requests:
- TokenRefreshRequest: { refreshToken }
– Responses:
- JwtResponse: { accessToken, type, refreshToken, id, username, email, roles }
- MessageResponse: { message }
- TokenRefreshResponse: { accessToken, type, refreshToken }
payload/request/TokenRefreshRequest.java
package com.bezkoder.spring.security.jwt.payload.request;
import javax.validation.constraints.NotBlank;
public class TokenRefreshRequest {
@NotBlank
private String refreshToken;
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
}
payload/response/JwtResponse.java
package com.bezkoder.spring.security.jwt.payload.response;
import java.util.List;
public class JwtResponse {
private String token;
private String type = "Bearer";
private String refreshToken;
private Long id;
private String username;
private String email;
private List<String> roles;
public JwtResponse(String accessToken, String refreshToken, Long id, String username, String email, List<String> roles) {
this.token = accessToken;
this.refreshToken = refreshToken;
this.id = id;
this.username = username;
this.email = email;
this.roles = roles;
}
// getters and setters
}
payload/response/TokenRefreshResponse.java
package com.bezkoder.spring.security.jwt.payload.response;
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;
}
// getters and setters
}
Renew JWT Token in Spring Boot
In the AuthController
class, we:
- update the method for
/signin
endpoint with Refresh Token - expose the POST API for creating new Access Token from received Refresh Token
controllers/AuthController.java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
JwtUtils jwtUtils;
@Autowired
RefreshTokenService refreshTokenService;
...
@PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
String jwt = jwtUtils.generateJwtToken(userDetails);
List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority())
.collect(Collectors.toList());
RefreshToken refreshToken = refreshTokenService.createRefreshToken(userDetails.getId());
return ResponseEntity.ok(new JwtResponse(jwt, refreshToken.getToken(), userDetails.getId(),
userDetails.getUsername(), userDetails.getEmail(), roles));
}
@PostMapping("/refreshtoken")
public ResponseEntity<?> refreshtoken(@Valid @RequestBody TokenRefreshRequest request) {
String requestRefreshToken = request.getRefreshToken();
return refreshTokenService.findByToken(requestRefreshToken)
.map(refreshTokenService::verifyExpiration)
.map(RefreshToken::getUser)
.map(user -> {
String token = jwtUtils.generateTokenFromUsername(user.getUsername());
return ResponseEntity.ok(new TokenRefreshResponse(token, requestRefreshToken));
})
.orElseThrow(() -> new TokenRefreshException(requestRefreshToken,
"Refresh token is not in database!"));
}
}
In refreshtoken()
method:
- Firstly, we get the Refresh Token from request data
- Next, get the
RefreshToken
object {id
,user
,token
,expiryDate
} from raw Token usingRefreshTokenService
- We verify the token (expired or not) basing on
expiryDate
field - Continue to use
user
field ofRefreshToken
object as parameter to generate new Access Token usingJwtUtils
- Return
TokenRefreshResponse
if everything is done - Or else, throw
TokenRefreshException
Create Refresh Token Service
Refresh Token class
This class has one-to-one relationship with User
class.
models/RefreshToken.java
package com.bezkoder.spring.security.jwt.models;
import java.time.Instant;
import javax.persistence.*;
@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;
//getters and setters
}
Refresh Token Repository
Before creating the service, we need RefreshTokenRepository
with finder methods:
repository/RefreshTokenRepository.java
package com.bezkoder.spring.security.jwt.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Repository;
import com.bezkoder.spring.security.jwt.models.RefreshToken;
import com.bezkoder.spring.security.jwt.models.User;
@Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
Optional<RefreshToken> findByToken(String token);
@Modifying
int deleteByUser(User user);
}
Refresh Token Service
After that, we have the RefreshTokenService
service which uses RefreshTokenRepository above for providing several useful methods:
findByToken()
: Find aRefreshToken
based on the natural id i.e the token itselfcreateRefreshToken()
: Create and return a new Refresh TokenverifyExpiration()
: Verify whether the token provided has expired or not. If the token was expired, delete it from database and throwTokenRefreshException
security/services/RefreshTokenRepository.java
package com.bezkoder.spring.security.jwt.security.services;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.bezkoder.spring.security.jwt.exception.TokenRefreshException;
import com.bezkoder.spring.security.jwt.models.RefreshToken;
import com.bezkoder.spring.security.jwt.repository.RefreshTokenRepository;
import com.bezkoder.spring.security.jwt.repository.UserRepository;
@Service
public class RefreshTokenService {
@Value("${bezkoder.app.jwtRefreshExpirationMs}")
private Long refreshTokenDurationMs;
@Autowired
private RefreshTokenRepository refreshTokenRepository;
@Autowired
private UserRepository userRepository;
public Optional<RefreshToken> findByToken(String token) {
return refreshTokenRepository.findByToken(token);
}
public RefreshToken createRefreshToken(Long userId) {
RefreshToken refreshToken = new RefreshToken();
refreshToken.setUser(userRepository.findById(userId).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());
}
}
Handle Token Refresh Exception
Now we need to create TokenRefreshException
class that extends RuntimeException
.
exception/TokenRefreshException.java
package com.bezkoder.spring.security.jwt.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.FORBIDDEN)
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));
}
}
Let’s do the final step. We’re gonna create a RestControllerAdvice
.
advice/TokenRefreshException.java
package com.bezkoder.spring.security.jwt.advice;
import java.util.Date;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import com.bezkoder.spring.security.jwt.exception.TokenRefreshException;
@RestControllerAdvice
public class TokenControllerAdvice {
@ExceptionHandler(value = TokenRefreshException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public ErrorMessage handleTokenRefreshException(TokenRefreshException ex, WebRequest request) {
return new ErrorMessage(
HttpStatus.FORBIDDEN.value(),
new Date(),
ex.getMessage(),
request.getDescription(false));
}
}
For more details about RestControllerAdvice
, please visit:
@RestControllerAdvice example in Spring Boot
Conclusion
Congratulation!
Today we’ve learned a more interesting thing about JWT Refresh Token in a Spring Boot example.
Despite we wrote a lot of code, I hope you will understand the overall idea of the application, and apply it in your project at ease.
The code in this post bases on previous article that you need to read first:
Spring Boot Token based Authentication with Spring Security & JWT
You can test this Rest API with:
– Axios Client: Axios Interceptors tutorial with Refresh Token example
– React Client:
– Vue Client:
– Angular Client:
For understanding the architecture deeply and grasp the overview more easier:
Spring Boot Architecture for JWT with Spring Security
You can also know how to deploy Spring Boot App on AWS (for free) with this tutorial.
Or Dockerize with :Docker Compose: Spring Boot and MySQL example
Happy learning! See you again.
Further Reading
More Practices:
- Spring Boot, Spring Data JPA – Building Rest CRUD API example
- Spring Boot Pagination & Filter example | Spring JPA, Pageable
- CRUD GraphQL APIs example with Spring Boot, MySQL & Spring JPA
- Spring Boot Rest XML example – Web service with XML Response
- Spring Boot File upload example with Multipart File
- @RestControllerAdvice example in Spring Boot
- Spring Boot @ControllerAdvice & @ExceptionHandler example
- @DataJpaTest example for Spring Data Repository Unit Test
Fullstack CRUD App:
– Spring Boot + Vue.js example
– Angular 8 + Spring Boot example
– Angular 10 + Spring Boot example
– Angular 11 + Spring Boot example
– Angular 12 + Spring Boot example
– Angular 13 + Spring Boot example
– Angular 14 + Spring Boot example
– React + Spring Boot example
Associations:
– JPA One To One example with Hibernate in Spring Boot
– JPA One To Many example with Hibernate and Spring Boot
– JPA Many to Many example with Hibernate in Spring Boot
Documentation: Spring Boot + Swagger 3 example (with OpenAPI 3)
Source Code
You can find the complete source code for this tutorial on Github.
Using HttpOnly Cookie instead: Spring Security Refresh Token with JWT
nice br0, but i cant integrate this example with httponly cookie, you can help me?
Hi, you can read following tutorial:
Spring Security Refresh Token with JWT
How would I implement refresh token if access token was implemented httponly?
Hi, you can read following tutorial:
Spring Security Refresh Token with JWT
Thanks for your code!
Can I use this refresh token to implement the “remember me” function?
Thanks!
Hi, why would RefreshToken entity class need the ID? Isn’t it just taking database space, because user_id will be unique itself in that table, so could it be used as primary key? Thanks.
You are the best man! Thank you again
Where are ErrorMessage used in TokenControlAdvice and refreshTokenRepository.deleteByUser used in RefreshTokenService defined?
You can check their source code to get the missing code 🙂
Hye,
Great post. So when you refreshed the token, I don’t see you saving it back to the Database why not?
“`
String token = jwtUtils.generateTokenFromUsername(user.getUsername());
return ResponseEntity.ok(new TokenRefreshResponse(token, requestRefreshToken));
“`
So you generated a new token and return it immediately. This implies that the new token doesn’t exist in the DB.
Hi,
io.jsonwebtoken.Jwts
manages it automatically.What do you mean? Jwts, calls the `Save` method behind the hood and persist the new token to the Database?
Please explain further.
Ah no, JWT gives you the advantage that we don’t need to check the Token in database, because we can just use cryptography to verify that the token is legitimate or not.
hello,
i dont know why did u use @OneToOne relationship ? because we store the token in DB everytime if the token is expired or if authorization is not respected.
so in DB for one user we have “N” Token
Hi, if the token is expired, we remove it from database. You can read
verifyExpiration()
function inRefreshTokenService
service.Thanks!
this the best site i’m looking for…thanks for the job you do for us
Good Article.
I want to use Custom password encoder by using AES+Base64 encryption.
Can you let me know how to do
Hi,
I’m new to JWT. Can anyone tell me if the below line of code really needed as I’m getting error 403 when the token is expired and deleted from the database
map(refreshTokenService::verifyExpiration)
Hi ,
Please if i want to refresh token with another method , and i want to decrease the backwards and forwards on the server by pushing notification to the front automatically when the token is expired , So thanks a lot if you write a tutorial and explain this when having time 😀
Hi,
Based on this section: https://datatracker.ietf.org/doc/html/rfc6819#section-5.2.2.3
We should also rotate refresh tokens.
So shouldn’t we provide fresh new refresh token instead old one each time user will post to the /refreshtoken?
Hello,
Can you help me to test expired refresh token from front end using interceptors, I followed your tutorial to make authentication with angular8 but I’m not able to change the function canActivate.
Thx
Hi, you can read the tutorial: Angular 12 Refresh Token with Interceptor and JWT example
Why not using a JWT for refresh token?
Hi, you can. I use
uuid
for easy-looking with JWT access token.If I use jwt as refresh token wont I be able to access API using refresh token?
Hi I am getting a 403 forbidden error when trying to access /refreshtoken?
Do you have any Idea on the silly mistake I could have made?
I was an idiot, feel free just to delete my comments!
I forgot the getters and setters on the TokenRefreshResponse!
Hello , thx u for the effort.
Do you have any tutorial how can i use the refresh token from front-end ?
using angular 11
Hi, here you are:
– Axios Client: Axios Interceptors tutorial with Refresh Token example
– React Client:
– Vue Client:
– Angular Client:
Hello,
When I post to the /refreshToken endpoint the new access and refresh tokens are equal to the previous, shouldn’t they be different?
Hi, only refresh token is the same as the previous 🙂
Generally, the refresh token has a long time to live. You don’t need to create a new refresh token everytime a user makes a
/refreshtoken
request. It helps us to reduce cost of database query (we store refresh token on a table).If I refresh the Token many times, I will get multiple Tokens, and these Tokens are different but it can be all authentic. Does this need to delete the old Token or not? If needed, how can I delete it?
Hi, I don’t think that Client implements the refresh Token that way :). Client should refresh it only when the access Token is expired.
Your question is correct!
During login, if login is success, then we have to do the following:
(not create immediately the refreshtoken like mentioned above).
check to see whether the token with that userid is existance or not.
1.1: if that token is existance and, is not expired
+ send back that token to Client. Never create new one.
1.2: if that token is not existance and. or it’s existance but expired
+ remove it from database
+ create new one like above mentioned
If you want to know more, please contact me!
Your sincerely!
Thanks you for this great tutorial!!!
It would great if you post an Angular tutorial with http interceptor using this Spring boot backend with refresh token.
Thanks in advance
Hi, you can read the tutorial: Angular 12 Refresh Token with Interceptor and JWT example
Thank you!
This post is great, I’ve learned a lot from your posts, but can you write a post to share how vue handles jwt refresh?
Hi, I will write the tutorial when having time 🙂
Thank you! I’m looking forward to it