Silent Token Refresh: Access vs Refresh Tokens with JWT Implementation

This article explains why silent token refresh is needed to prevent sudden logouts, demonstrates backend automatic token renewal using Java JWT (jjwt 0.9.1) with Maven dependencies, details the dual-token (AT/RT) frontend pattern for security and convenience, and discusses handling edge cases like long-idle form submissions that cause 401 errors.

Architect's Guide
Architect's Guide
Architect's Guide
Silent Token Refresh: Access vs Refresh Tokens with JWT Implementation

Why Silent Token Refresh Is Needed

When a user performs actions on a system page, the session may suddenly expire and redirect to the login page. The root cause is token expiration leading to identity invalidation. The solution is automatic token renewal: either the backend generates a new token when the old one is about to expire and returns it in the response header, or a scheduled task extends the token's expiry time.

Backend Automatic Token Refresh

The backend checks the token's expiration time during validation. If the remaining time is below a threshold, it creates a new token from the existing token's claims and places it in the response header. The frontend intercepts the response, compares the new token with the stored one, and updates local storage if they differ.

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>

JwtUtil Class

The utility class uses HS256 signing with a configurable secret key (default "qx") and a default TTL of 24 hours (60*60*1000*24 ms). It provides overloaded createJWT methods accepting subject, optional TTL, and optional ID. The parseJWT method validates and returns claims. A helper generalKey decodes the Base64 secret into an AES SecretKeySpec.

public class JwtUtil {
  public static final Long JWT_TTL = 60 * 60 * 1000 * 24;
  public static final String JWT_KEY = "qx";
  public static String getUUID() { ... }
  public static String createJWT(String subject) { ... }
  public static String createJWT(String subject, Long ttlMillis) { ... }
  public static String createJWT(String id, String subject, Long ttlMillis) { ... }
  private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) { ... }
  public static SecretKey generalKey() { ... }
  public static Claims parseJWT(String jwt) throws Exception { ... }
}

Unit Test Verification

A test creates a token with subject "1735209949551763457", prints the token, extracts its expiration date, and calculates the difference between expiration (1703651262000 ms) and current time (1703564863035 ms) yielding 86,398,965 ms — close to the expected 86,400,000 ms for 24 hours. This confirms the token's expiry is correctly set.

Frontend Token Renewal with Dual Tokens (AT/RT)

The frontend-driven approach uses two tokens: an Access Token (AT) with a short expiry (e.g., 30 minutes) carried on every request, and a Refresh Token (RT) with a longer expiry (e.g., 7 days) used only against the auth service to obtain a new AT. This limits exposure of the AT, reducing hijack risk, while the RT's limited scope increases convenience. The author states this is a standard security practice akin to HTTPS over HTTP.

Edge Case: Long-Idle Form Submission

If a user spends a long time filling a form without sending requests, the token may expire. When the form is submitted, the backend returns 401. For the backend-only solution, the frontend should cache the form data locally, redirect to login, then restore the data after successful login. For the dual-token frontend solution, both AT and RT may have expired; the frontend must monitor RT expiry and proactively refresh it, or implement a draft-saving mechanism similar to the backend approach.

Further Reading

For more on stateless JWT usage, see the referenced link: https://t.zsxq.com/15xye5IWG.

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.

JavasecurityauthenticationJWTaccess tokenrefresh tokentoken refreshjjwt
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

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.