Secure API Design for Microservices: Anti‑Tampering, Anti‑Replay, and Authentication Strategies
This article explains how to protect microservice APIs from tampering and replay attacks by using HTTPS, request signing, nonce‑timestamp mechanisms, and AppId/AppSecret authentication, and demonstrates a complete Java/Spring implementation with a responsibility‑chain based verification filter.
When exposing microservice APIs to third parties, ensuring their security requires protecting against tampering and replay attacks.
1. What is a Secure Interface
A secure API must provide anti‑tampering and anti‑replay capabilities.
1.1 Tampering Problem
Because HTTP is stateless, an attacker can capture a request such as http://localhost/api/user/recharge?user_id=1001&amount=10 and modify parameters to add arbitrary balance.
1.1.1 Solution
Two common solutions are:
Use HTTPS to encrypt the transmission.
Sign request parameters on the server side and verify the signature.
The signing process involves the client generating a signature (sign1) and sending it with the request; the server recomputes the signature (sign2) and compares the two values.
1.2 Replay Problem
Replay attacks reuse a captured request without modification, causing duplicate data or overwhelming slow‑query endpoints.
1.2.1 Solution
Implement a nonce + timestamp scheme: each request includes a unique random string and the current timestamp, both of which are signed. The server checks that the timestamp is within an acceptable window (e.g., 60 seconds) and that the nonce has not been used before (typically stored in Redis).
2. Authentication Scheme
Access to public APIs is restricted using AppId and AppSecret . The client sends AppId , timestamp , nonce , and other parameters; the server retrieves the corresponding AppSecret , appends it to the sorted parameter string, hashes the result (MD5), and compares it with the client‑provided signature.
2.1 Signing Process
Server provides a pair of AppId and AppSecret to the client.
Client builds a string StringA by sorting all parameters (including timestamp , nonce , AppId ) as key1=value1&key2=value2… .
Append AppSecret to obtain StringB .
Hash StringB (MD5) and convert to uppercase to get the signature sign , then send it with the request.
Server recomputes the signature and validates it together with timestamp and nonce.
3. Code Implementation
The following Java/Spring code demonstrates the complete solution.
3.1 Generating AppId and AppSecret
private static String getAppKey() {
long num = IdUtils.nextId();
StringBuilder sb = new StringBuilder();
do {
int remainder = (int) (num % 62);
sb.insert(0, BASE62_CHARACTERS.charAt(remainder));
num /= 62;
} while (num != 0);
return sb.toString();
}Example output: appKey=6iYWoL2hBk9, appSecret=5de8bc4d8278ed4f14a3490c0bdd5cbe369e8ec9
3.2 API Authenticator Interface
public interface ApiAuthenticator {
AuthenticatorResult auth(ServerWebExchange request);
}3.3 Gateway Filter
@Component
@Slf4j
public class ApiAuthenticatorFilter implements GlobalFilter, Ordered {
@Override
public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ApiAuthenticator apiAuthenticator = getApiAuthenticator(rawPath);
AuthenticatorResult result = apiAuthenticator.auth(exchange);
if (!result.isResult()) {
return Mono.error(new HttpServerErrorException(HttpStatus.METHOD_NOT_ALLOWED, result.getMessage()));
}
return chain.filter(exchange);
}
// ... method to resolve authenticator based on path
}3.4 Responsibility‑Chain Verification
Each verification step (parameter check, nonce check, timestamp check, signature check) is implemented as a SecurityVerificationHandler with an order value. The SecurityVerificationChain iterates over the handlers and stops on the first failure.
public interface SecurityVerificationHandler extends Ordered {
AuthenticatorResult handler(ProtectedRequest protectedRequest);
}Example handler for nonce verification:
@Component
public class NonceVerificationHandler implements SecurityVerificationHandler {
@Value("${dailymart.sign.timeout:60000}")
private long expireTime;
@Resource
private DistributedCache distributedCache;
@Override
public AuthenticatorResult handler(ProtectedRequest request) {
String nonce = request.getRequestHeader().getNonce();
if (distributedCache.hasKey("x-nonce-" + nonce)) {
return new AuthenticatorResult(false, "请勿重复提交请求");
}
distributedCache.put("x-nonce-" + nonce, nonce, expireTime);
return new AuthenticatorResult(true, "");
}
@Override
public int getOrder() { return 3; }
}The chain aggregates results and returns success only if all handlers pass.
Conclusion
The article demonstrates how to secure external microservice APIs by combining HTTPS, request signing, nonce‑timestamp anti‑replay mechanisms, and an AppId/AppSecret authentication model, and provides a modular, extensible Java/Spring implementation using the responsibility‑chain pattern.
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.