跪拜 Guibai
← All articles
Frontend · JavaScript

Five Gears That Hold a Login Session Together Without a Stateful Server

By Asize ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Authentication is the first gate in nearly every web app, and getting it wrong silently breaks user sessions. This walkthrough makes the full chain explicit — token, storage, state, transport, and routing — so a developer can debug any link in that chain rather than treating login as a black box.

Summary

HTTP remembers nothing, so a logged-in session must be rebuilt on every request. This walkthrough assembles a complete authentication loop inside a React frontend — no real backend required — using JWT signing and verification, Zustand for global state, axios interceptors, route guards, and localStorage persistence. A mock server handles the sign/verify cycle, while the frontend code demonstrates the exact sequence that keeps a user recognized across page refreshes and route changes.

The five pieces form a chain: a JWT token acts as a portable, verifiable identity card; localStorage survives refreshes so the token isn't lost; a Zustand store pulls that token into React's reactive memory; an axios request interceptor silently attaches it to every API call; and a route guard component blocks unauthenticated page visits before they render.

Each mechanism solves one specific failure mode — token loss on refresh, forgotten headers on API calls, unprotected routes rendering for logged-out users — and the mock-driven setup means the entire flow can be developed and tested before a real backend exists.

Takeaways
JWT replaces server-side session storage with a signed token the client carries; any server holding the secret can verify it, which eliminates the session-sharing problem in distributed deployments.
localStorage persists the token across page refreshes and browser restarts, which is why a user stays logged in even though HTTP itself is stateless.
A Zustand store reads the token from localStorage on initialization and exposes it to any component through a hook, removing the need for Context providers and prop drilling.
An axios request interceptor automatically prepends `Authorization: Bearer <token>` to every outgoing request so individual API calls never need to manage auth headers manually.
A route guard component checks the token from the Zustand store before rendering protected pages; missing tokens redirect to the login page with a `replace` history entry.
The mock server uses `jwt.sign` with a secret and expiry to issue tokens and `jwt.verify` to decode them, simulating the full sign/verify cycle without a backend.
Form validation runs inside a `useEffect` that watches form data, recalculates errors on every keystroke, and disables the submit button until all fields pass.
A `cancelled` flag in the homepage's `useEffect` cleanup prevents state updates after the component unmounts, avoiding React warnings during async token verification.
vite-plugin-mock loads mock files from a directory and registers them as HTTP endpoints, letting the frontend run the complete auth flow before a real server exists.
Conclusions

Treating authentication as a chain of five independent mechanisms — token, storage, state, transport, routing — makes failures easier to isolate than treating it as one opaque feature.

The mock-driven approach decouples frontend auth development from backend availability; the same code switches to a real server by changing a base URL and disabling the mock plugin.

Zustand's lack of Provider boilerplate matters most in auth, where the token must be readable from deeply nested components, navigation bars, and route guards without wiring props through every layer.

The `cancelled` flag pattern in useEffect is under-taught but essential for any component that fires an async request on mount and can unmount before the response arrives.

Reading the token directly from localStorage inside the axios interceptor, rather than from the Zustand store, keeps the interceptor independent of React's component tree and avoids circular dependencies.

Concepts & terms
JWT (JSON Web Token)
A compact, URL-safe token format where a JSON payload is base64-encoded and signed with a secret. The server can verify the signature to confirm the payload hasn't been tampered with, without storing any session state.
Bearer Token
An authentication token defined by RFC 6750, sent in the `Authorization` header as `Bearer <token>`. Possession of the token is treated as proof of identity, so protecting the token from leaks is critical.
Zustand
A lightweight React state management library built around a single `create` function that returns a custom hook. Components subscribe to state slices and re-render only when those slices change, with no Provider wrapping required.
Axios interceptor
A middleware hook in the axios HTTP library that runs on every request or response. Request interceptors can modify headers (e.g., attach auth tokens); response interceptors can transform data before it reaches calling code.
Route guard
A component that wraps protected routes and checks authentication state before rendering children. Unauthenticated users are redirected to a login page, often preserving the intended destination via location state.
localStorage
A browser API for storing key-value string pairs that persist across page sessions and browser restarts. It is scoped by origin and is commonly used to store JWT tokens on the client.
vite-plugin-mock
A Vite plugin that intercepts HTTP requests during development and serves responses from local mock files, allowing frontend development to proceed without a running backend server.
Cancelled flag pattern
A React useEffect pattern where a local boolean flag is set to `true` in the cleanup function; async response handlers check the flag before calling setState to avoid updating unmounted components.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗