How to Implement Seamless Token Refresh in Java Backend with JWT

This article explains why silent token refresh is needed, outlines backend and front‑end strategies for automatically renewing JWTs, provides a complete Java implementation with Maven dependencies, utility methods, unit tests, and discusses handling edge cases such as long‑idle forms.

Architecture Digest
Architecture Digest
Architecture Digest
How to Implement Seamless Token Refresh in Java Backend with JWT

Why Need Silent Token Refresh?

When a user performs operations and the token expires, the system abruptly redirects to the login page.

Root cause is token expiration leading to identity loss.

Solution: automatically refresh the token before it expires.

Backend approach: during permission verification, generate a new token if the current one is about to expire, place it in the response header, and let the front‑end replace the stored token when it detects a change.

Implementation steps :

Add Maven dependencies:

<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.5.1</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.33</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>

Create a JwtUtil class that defines token TTL, secret key, UUID generator, and methods to build, sign, and parse JWTs.

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

public class JwtUtil {
    public static final Long JWT_TTL = 60 * 60 * 1000 * 24; // 24 hours
    public static final String JWT_KEY = "qx";

    private static String getUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String createJWT(String subject) {
        JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
        return builder.compact();
    }

    public static String createJWT(String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());
        return builder.compact();
    }

    public static String createJWT(String id, String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);
        return builder.compact();
    }

    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if (ttlMillis == null) {
            ttlMillis = JWT_TTL;
        }
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        return Jwts.builder()
                .setId(uuid)
                .setSubject(subject)
                .setIssuer("sg")
                .setIssuedAt(now)
                .signWith(signatureAlgorithm, secretKey)
                .setExpiration(expDate);
    }

    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JWT_KEY);
        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
    }

    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }
}

Write a unit test to generate a token, retrieve its expiration date, and compare it with the current time.

@Test
void test() throws Exception {
    String token = JwtUtil.createJWT("1735209949551763457");
    System.out.println("Token: " + token);
    Date tokenExpirationDate = getTokenExpirationDate(token);
    System.out.println(tokenExpirationDate);
    long exp = tokenExpirationDate.getTime();
    long cur = System.currentTimeMillis();
    System.out.println(exp - cur);
}

public static Date getTokenExpirationDate(String token) {
    try {
        SecretKey secretKey = JwtUtil.generalKey();
        Claims claims = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody();
        return claims.getExpiration();
    } catch (ExpiredJwtException | SignatureException e) {
        throw new RuntimeException("Invalid token", e);
    }
}

Front‑end token renewal: use a double‑token scheme (access‑token AT and refresh‑token RT). AT is short‑lived and sent with every request; RT lives longer and is only used to obtain a new AT when the latter is about to expire, reducing hijack risk while keeping user experience smooth.

Pure‑backend approach: only an AT exists; the backend checks its remaining lifetime and, if necessary, extends its expiration and returns the new token in the response header.

Open questions and considerations :

If a user fills a long‑idle form and submits after the AT has expired, the server returns 401. The front‑end should detect this, preserve the form data locally, redirect to login, and restore the data after re‑authentication.

For the double‑token model, schedule a refresh of the RT before it expires or implement a draft‑saving mechanism on the client side.

Token expiration illustration
Token expiration illustration
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaspringAuthenticationJWTtoken refresh
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.