How to Secure API Calls with Tokens, Timestamps, and Signatures in Spring Boot
This article explains how to protect API data exchange by using access tokens, timestamps, MD5 signatures, Redis caching, and a custom @NotRepeatSubmit annotation to prevent replay attacks and duplicate submissions in a Spring Boot backend.
In real‑world projects, APIs often need to exchange data with third‑party systems, and ensuring the confidentiality and integrity of that data is essential. Beyond using HTTPS, the article introduces a set of security mechanisms—token, timestamp, and sign—that can be combined to protect API calls.
1. Token Overview
A token (access token) identifies the caller and reduces the need to transmit username and password repeatedly. After a client registers with the server, it receives an appId and a key. The key is used for signing and must be stored securely on the client side.
API Token : Used for endpoints that do not require user login (e.g., login, registration, basic data). The client sends appId, timestamp, and sign where sign = MD5(timestamp + key).
User Token : Used after a user logs in. The client sends username and password to obtain a user token.
Tokens are usually UUID strings stored in Redis as keys, with associated metadata (app info, user info) as values. Tokens can be one‑time or have a configurable validity period.
2. Timestamp
The timestamp is the client’s current epoch time. It helps mitigate replay and DoS attacks by limiting the time window in which a request is considered valid. The server checks that the difference between the current time and the provided timestamp is within a configurable threshold (e.g., 5 minutes).
3. Sign (Signature)
The sign parameter prevents parameter tampering. The signature is generated by concatenating all non‑empty request parameters (sorted by name), the token, timestamp, nonce, and the server‑side secret key, then applying an MD5 hash:
signString = sortedParams + token + timestamp + nonce + key
sign = MD5(signString)During verification, the server recomputes the signature and compares it with the received sign. If they match, the request is trusted.
4. Preventing Duplicate Submissions
A custom annotation @NotRepeatSubmit can be placed on controller methods. When the interceptor detects this annotation, it stores the sign in Redis with the same expiration as the request timeout. Subsequent requests with the same sign are rejected, eliminating accidental or malicious duplicate submissions.
5. Usage Flow
The client requests an API token by sending appId, timestamp, and sign (where sign = MD5(appId + timestamp + key)).
With the API token, the client can call public endpoints.
When a user logs in, the client sends credentials to obtain a user token.
For protected endpoints, the client includes token, timestamp, nonce, and sign in the request headers.
The server validates the timestamp, checks token existence in Redis, verifies the signature, and optionally enforces the duplicate‑submission rule.
6. Code Samples
The article provides complete Spring Boot code snippets, including:
Maven dependencies for Spring Boot, Redis, and Lombok. RedisConfiguration that creates a JedisConnectionFactory and a RedisTemplate with JSON serialization. TokenController with endpoints /api_token (API token) and /user_token (user token), including parameter validation, signature verification, token generation, and Redis storage. WebMvcConfiguration that registers TokenInterceptor for all /api/** paths except the token‑generation endpoint. TokenInterceptor that extracts headers, validates timestamp, checks token existence, recomputes the signature, and enforces the @NotRepeatSubmit rule.
Utility classes: MD5Util for MD5 hashing, ApiUtil for building the canonical sign string and retrieving the custom annotation, and a set of data classes ( AccessToken, AppInfo, TokenInfo, UserInfo, ApiCodeEnum, ApiResult, ApiResponse).
The custom annotation @NotRepeatSubmit with a configurable expiration time (default 5000 ms).
7. ThreadLocal Usage
To avoid passing the user object through every method, the article introduces a ThreadLocalUtil that stores a map of context variables per thread. After the interceptor validates the token, the user information can be placed into ThreadLocalUtil, making it accessible from controllers, services, or DAOs without additional parameters.
8. Final Remarks
The presented approach offers a practical, lightweight security layer for API communication. For higher security requirements, developers can augment the MD5 signature with RSA, RSA2, or AES encryption, acknowledging the additional CPU overhead.
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.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.
