Automatic JWT Token Renewal to Prevent User Re‑Login
This article explains how to automatically renew JWT tokens in a front‑end/back‑end separated system by storing tokens in a cache, checking them in a filter, and refreshing them when expired, thereby preventing frequent logins and preserving form data.
Introduction
In a front‑end/back‑end separated architecture, after a successful login the back‑end issues a JWT token that the front‑end (e.g., Vue) stores in LocalStorage. Subsequent requests send this token in the request header, where a back‑end filter validates its expiration and redirects to login if it is invalid.
Because JWT tokens usually contain basic user information, their expiration time is set short for security, which can cause frequent logins, especially when users spend a long time filling complex forms.
This article demonstrates how to automatically renew the token without user awareness, avoiding repeated logins and loss of form data.
Implementation Principle
The automatic renewal works as follows:
After login, store the generated JWT token as both key and value in a cache (e.g., Redis) with an expiration time twice the token’s lifetime.
For each request, a JWT filter checks the token from the request header. If the token is invalid, an exception is thrown.
The filter retrieves the cached token and handles three cases:
If the cached token does not exist, the user session is considered timed out and a re‑login is required.
If the cached token exists and is still valid, no action is needed.
If the cached token exists but is expired, the back‑end generates a new JWT token, overwrites the cache value, and resets the cache expiration.
Core principle: The token sent in the request header remains unchanged; its validity is determined by the token stored in the cache.
Code Implementation (Pseudo‑code)
Issue token after successful login and set its expiration.
...<br/>SysUser sysUser = userService.getUser(username,password);<br/><span style="color: #569CD6; line-height: 26px">if</span>(<span style="color: #569CD6; line-height: 26px">null</span> !== sysUser){<br/> String token = JwtUtil.sign(sysUser.getUsername(), <br/>sysUser.getPassword());<br/>}<br/>...<br/><br/><br/><span style="line-height: 26px"><span style="color: #569CD6; line-height: 26px">public</span> <span style="color: #569CD6; line-height: 26px">static</span> String <span style="line-height: 26px">sign</span><span style="line-height: 26px">(String username, String secret)</span> </span>{<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">//设置token有效期为30分钟</span><br/> Date date = <span style="color: #569CD6; line-height: 26px">new</span> Date(System.currentTimeMillis() + <span style="color: #B8D7A3; line-height: 26px">30</span> * <span style="color: #B8D7A3; line-height: 26px">60</span> * <span style="color: #B8D7A3; line-height: 26px">1000</span>);<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">//使用HS256生成token,密钥则是用户的密码</span><br/> Algorithm algorithm = Algorithm.HMAC256(secret);<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">// 附带username信息</span><br/> <span style="color: #569CD6; line-height: 26px">return</span> JWT.create().withClaim(<span style="color: #D69D85; line-height: 26px">"username"</span>, username).withExpiresAt(date).sign(algorithm);<br/>}<br/>Store the token in Redis and set the Redis expiration to twice the token’s lifetime.
Sting tokenKey = <span style="color: #D69D85; line-height: 26px">"sys:user:token"</span> + token;<br/>redisUtil.set(tokenKey, token);<br/>redisUtil.expire(tokenKey, <span style="color: #B8D7A3; line-height: 26px">30</span> * <span style="color: #B8D7A3; line-height: 26px">60</span> * <span style="color: #B8D7A3; line-height: 26px">2</span>);<br/>Filter validates the token’s validity.
<span style="line-height: 26px"><span style="color: #569CD6; line-height: 26px">public</span> <span style="color: #569CD6; line-height: 26px">void</span> <span style="line-height: 26px">doFilter</span><span style="line-height: 26px">(ServletRequest req, ServletResponse res, FilterChain chain)</span> <span style="color: #569CD6; line-height: 26px">throws</span> IOException, ServletException </span>{<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">//从header中获取token</span><br/> String token = httpServletRequest.getHeader(<span style="color: #D69D85; line-height: 26px">"token"</span>)<br/> <span style="color: #569CD6; line-height: 26px">if</span>(<span style="color: #569CD6; line-height: 26px">null</span> == token){<br/> <span style="color: #569CD6; line-height: 26px">throw</span> <span style="color: #569CD6; line-height: 26px">new</span> RuntimeException(<span style="color: #D69D85; line-height: 26px">"illegal request,token is necessary!"</span>)<br/> }<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">//解析token获取用户名</span><br/> String username = JwtUtil.getUsername(token);<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">//根据用户名获取用户实体,在实际开发中从redis取</span><br/> User user = userService.findByUser(username);<br/> <span style="color: #569CD6; line-height: 26px">if</span>(<span style="color: #569CD6; line-height: 26px">null</span> == user){<br/> <span style="color: #569CD6; line-height: 26px">throw</span> <span style="color: #569CD6; line-height: 26px">new</span> RuntimeException(<span style="color: #D69D85; line-height: 26px">"illegal request,token is Invalid!"</span>)<br/> }<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">//校验token是否失效,自动续期</span><br/> <span style="color: #569CD6; line-height: 26px">if</span>(!refreshToken(token,username,user.getPassword())){<br/> <span style="color: #569CD6; line-height: 26px">throw</span> <span style="color: #569CD6; line-height: 26px">new</span> RuntimeException(<span style="color: #D69D85; line-height: 26px">"illegal request,token is expired!"</span>)<br/> }<br/> ...<br/>}<br/>Automatic token renewal logic.
<span style="line-height: 26px"><span style="color: #569CD6; line-height: 26px">public</span> <span style="color: #569CD6; line-height: 26px">boolean</span> <span style="line-height: 26px">refreshToken</span><span style="line-height: 26px">(String token, String userName, String passWord)</span> </span>{<br/> Sting tokenKey = <span style="color: #D69D85; line-height: 26px">"sys:user:token"</span> + token ;<br/> String cacheToken = String.valueOf(redisUtil.get(tokenKey));<br/> <span style="color: #569CD6; line-height: 26px">if</span> (StringUtils.isNotEmpty(cacheToken)) {<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">// 校验token有效性,注意需要校验的是缓存中的token</span><br/> <span style="color: #569CD6; line-height: 26px">if</span> (!JwtUtil.verify(cacheToken, userName, passWord)) {<br/> String newToken = JwtUtil.sign(userName, passWord);<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">// 设置超时时间</span><br/> redisUtil.set(tokenKey, newToken) ;<br/> redisUtil.expire(tokenKey, <span style="color: #B8D7A3; line-height: 26px">30</span> * <span style="color: #B8D7A3; line-height: 26px">60</span> * <span style="color: #B8D7A3; line-height: 26px">2</span>);<br/> }<br/> <span style="color: #569CD6; line-height: 26px">return</span> <span style="color: #569CD6; line-height: 26px">true</span>;<br/> }<br/> <span style="color: #569CD6; line-height: 26px">return</span> <span style="color: #569CD6; line-height: 26px">false</span>;<br/>}<br/>...<br/><br/><span style="line-height: 26px"><span style="color: #569CD6; line-height: 26px">public</span> <span style="color: #569CD6; line-height: 26px">static</span> <span style="color: #569CD6; line-height: 26px">boolean</span> <span style="line-height: 26px">verify</span><span style="line-height: 26px">(String token, String username, String secret)</span> </span>{<br/> <span style="color: #569CD6; line-height: 26px">try</span> {<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">// 根据密码生成JWT效验器</span><br/> Algorithm algorithm = Algorithm.HMAC256(secret);<br/> JWTVerifier verifier = JWT.require(algorithm).withClaim(<span style="color: #D69D85; line-height: 26px">"username"</span>, username).build();<br/> <span style="color: #57A64A; font-style: italic; line-height: 26px">// 效验TOKEN</span><br/> DecodedJWT jwt = verifier.verify(token);<br/> <span style="color: #569CD6; line-height: 26px">return</span> <span style="color: #569CD6; line-height: 26px">true</span>;<br/> } <span style="color: #569CD6; line-height: 26px">catch</span> (Exception exception) {<br/> <span style="color: #569CD6; line-height: 26px">return</span> <span style="color: #569CD6; line-height: 26px">false</span>;<br/> }<br/>}<br/>The JWT operations rely on com.auth0.java-jwt and the custom JwtUtil utility class.
Conclusion
The essential idea is that the token in the request header stays unchanged, while its validity is verified against the cached token; never validate the header token directly. Understanding the principle is more important than the concrete code.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
