As a backend developer, I am facing JWT token expired exception (JWTExpiredException) at functional testing 🤯. I think many of us facing same problem in the daily life coding 🤬. Want to come out with this problem. Then this article is for you.🤩

Let’s dive in the code without wasting your time.⌚ You can download the code from Git below.✨
Create JWT token with No Expiration Spring Boot
First of all let’s move to the application.properties file
1 2 3 4 5 6 7 |
jwt.secret = dasjs jwt.expirationDateInMs=50000 jwt.refreshExpirationDateInMs=9000000 spring.datasource.url=jdbc:mysql://localhost/bootjwt?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false spring.datasource.username=root spring.datasource.password= spring.jpa.hibernate.ddl-auto=create-drop |
We are using simple Spring Boot JWT web token example and as you can see that the jwt.expirationDateInMs=50000 define the normal token expiration and jwt.refreshExpirationDateInMs=9000000 define the refresh token expiration.
Now its time to configure the refresh token with our service class (JWTUtil)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
package com.dasjs.springbootsecurity.config; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.UnsupportedJwtException; @Service public class JwtUtil { private String secret; private int jwtExpirationInMs; private int refreshExpirationDateInMs; @Value("${jwt.secret}") public void setSecret(String secret) { this.secret = secret; } @Value("${jwt.expirationDateInMs}") public void setJwtExpirationInMs(int jwtExpirationInMs) { this.jwtExpirationInMs = jwtExpirationInMs; } @Value("${jwt.refreshExpirationDateInMs}") public void setRefreshExpirationDateInMs(int refreshExpirationDateInMs) { this.refreshExpirationDateInMs = refreshExpirationDateInMs; } public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); Collection<? extends GrantedAuthority> roles = userDetails.getAuthorities(); if (roles.contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) { claims.put("isAdmin", true); } if (roles.contains(new SimpleGrantedAuthority("ROLE_USER"))) { claims.put("isUser", true); } return doGenerateToken(claims, userDetails.getUsername()); } private String doGenerateToken(Map<String, Object> claims, String subject) { return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + jwtExpirationInMs)) .signWith(SignatureAlgorithm.HS512, secret).compact(); } public String doGenerateRefreshToken(Map<String, Object> claims, String subject) { return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + refreshExpirationDateInMs)) .signWith(SignatureAlgorithm.HS512, secret).compact(); } public boolean validateToken(String authToken) { try { Jws<Claims> claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken); return true; } catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException ex) { throw new BadCredentialsException("INVALID_CREDENTIALS", ex); } catch (ExpiredJwtException ex) { throw ex; } } public String getUsernameFromToken(String token) { Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); return claims.getSubject(); } public List<SimpleGrantedAuthority> getRolesFromToken(String token) { Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); List<SimpleGrantedAuthority> roles = null; Boolean isAdmin = claims.get("isAdmin", Boolean.class); Boolean isUser = claims.get("isUser", Boolean.class); if (isAdmin != null && isAdmin) { roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")); } if (isUser != null && isAdmin) { roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); } return roles; } } |
Now we have to implement the filter for JWT authentication. In this filter named CustomJwtAuthenticationFilter, If JWT token expired during the testing then we have to set isRefreshToken set to true.
Create custom Authorization Filter
When the token is expired then we have to get claims from the ExpiredJWTException and store it in HttpRequest. These claims would be use to create refresh JWT token. And we have to set Security context by create the UsernamePasswordAuthenticationToken with null values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
package com.dasjs.springbootsecurity.config; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import io.jsonwebtoken.ExpiredJwtException; @Component public class CustomJwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { try{ // JWT Token is in the form "Bearer token". Remove Bearer word and // get only the Token String jwtToken = extractJwtFromRequest(request); if (StringUtils.hasText(jwtToken) && jwtTokenUtil.validateToken(jwtToken)) { UserDetails userDetails = new User(jwtTokenUtil.getUsernameFromToken(jwtToken), "", jwtTokenUtil.getRolesFromToken(jwtToken)); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); // After setting the Authentication in the context, we specify // that the current user is authenticated. So it passes the // Spring Security Configurations successfully. SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } else { System.out.println("Cannot set the Security Context"); } }catch(ExpiredJwtException ex) { String isRefreshToken = request.getHeader("isRefreshToken"); String requestURL = request.getRequestURL().toString(); // allow for Refresh Token creation if following conditions are true. if (isRefreshToken != null && isRefreshToken.equals("true") && requestURL.contains("refreshtoken")) { allowForRefreshToken(ex, request); } else request.setAttribute("exception", ex); } catch(BadCredentialsException ex) { request.setAttribute("exception", ex); } chain.doFilter(request, response); } private void allowForRefreshToken(ExpiredJwtException ex, HttpServletRequest request) { // create a UsernamePasswordAuthenticationToken with null values. UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( null, null, null); // After setting the Authentication in the context, we specify // that the current user is authenticated. So it passes the // Spring Security Configurations successfully. SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); // Set the claims so that in controller we will be using it to create // new JWT request.setAttribute("claims", ex.getClaims()); } private String extractJwtFromRequest(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7, bearerToken.length()); } return null; } } |
Now we can move to the create the refresh token in AuthenticationController.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
package com.dasjs.springbootsecurity.controller; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.dasjs.springbootsecurity.config.CustomUserDetailsService; import com.dasjs.springbootsecurity.config.JwtUtil; import com.dasjs.springbootsecurity.model.AuthenticationRequest; import com.dasjs.springbootsecurity.model.AuthenticationResponse; import com.dasjs.springbootsecurity.model.UserDTO; import io.jsonwebtoken.impl.DefaultClaims; @RestController public class AuthenticationController { @Autowired private AuthenticationManager authenticationManager; @Autowired private CustomUserDetailsService userDetailsService; @Autowired private JwtUtil jwtUtil; @RequestMapping(value = "/authenticate", method = RequestMethod.POST) public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception { try { authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( authenticationRequest.getUsername(), authenticationRequest.getPassword())); } catch (DisabledException e) { throw new Exception("USER_DISABLED", e); } catch (BadCredentialsException e) { throw new Exception("INVALID_CREDENTIALS", e); } UserDetails userdetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername()); String token = jwtUtil.generateToken(userdetails); return ResponseEntity.ok(new AuthenticationResponse(token)); } @RequestMapping(value = "/register", method = RequestMethod.POST) public ResponseEntity<?> saveUser(@RequestBody UserDTO user) throws Exception { return ResponseEntity.ok(userDetailsService.save(user)); } @RequestMapping(value = "/refreshtoken", method = RequestMethod.GET) public ResponseEntity<?> refreshtoken(HttpServletRequest request) throws Exception { // From the HttpRequest get the claims DefaultClaims claims = (io.jsonwebtoken.impl.DefaultClaims) request.getAttribute("claims"); Map<String, Object> expectedMap = getMapFromIoJsonwebtokenClaims(claims); String token = jwtUtil.doGenerateRefreshToken(expectedMap, expectedMap.get("sub").toString()); return ResponseEntity.ok(new AuthenticationResponse(token)); } public Map<String, Object> getMapFromIoJsonwebtokenClaims(DefaultClaims claims) { Map<String, Object> expectedMap = new HashMap<String, Object>(); for (Entry<String, Object> entry : claims.entrySet()) { expectedMap.put(entry.getKey(), entry.getValue()); } return expectedMap; } } |
That’s it. Now you can check the demo of this example.
Demo Screenshot
Register the user

Authenticate the URL

Access the Admin section with normal token

Create Refresh Token
