How to Build Secure, Idempotent APIs: Keys, Tokens, and Anti‑Replay Strategies

This article explains how to design secure and reliable APIs for third‑party integration by covering API key generation, AK/SK authentication, callback URLs, permission models, token mechanisms, signature creation, replay‑attack prevention, HTTPS encryption, rate limiting, logging, data masking, idempotency, versioning, standardized response formats and documentation tools.

Architect
Architect
Architect
How to Build Secure, Idempotent APIs: Keys, Tokens, and Anti‑Replay Strategies

API Design Overview

When providing interfaces to third‑party systems, data security, integrity, freshness and idempotency are essential; the design must include API keys, authentication, callback URLs and permission control.

1. API Key and Authentication

1.1 API Key Generation and Allocation

Each third‑party application receives an Access Key (AK) to identify the client and a Secret Key (SK) to sign and encrypt requests.

Access Key (AK) : identifier similar to a username.

Secret Key (SK) : confidential key used for signing, similar to a password.

1.2 Authentication

Clients create a signature (typically HMAC‑SHA256) from AK and request parameters and send it in headers; the server verifies the signature with the stored SK.

2. Callback Address

Third‑party apps provide a callback URL to receive asynchronous notifications, e.g., payment results.

3. Permission Control and Allocation

3.1 Permission Model

Permissions are assigned per appId, appKey and appSecret, allowing fine‑grained access.

appId : unique application identifier.

appKey : public key used in requests.

appSecret : private key paired with appKey for signing.

3.2 Token Mechanism

Clients send appKey and appSecret to obtain an accessToken; subsequent requests must include the token.

Client sends request with appKey and appSecret.

Server validates and returns a token.

Client includes token in later requests.

4. Signature and Replay‑Attack Prevention

4.1 Signature Rules

Requests include appId, appSecret, timestamp, nonce and a signature generated from these values to ensure integrity.

Timestamp : limits request validity (e.g., 5 minutes).

Nonce : random value to prevent duplicate submissions.

Signature : computed from parameters and appSecret.

4.2 Anti‑Replay

Server checks timestamp freshness and nonce uniqueness; repeated nonces are rejected.

5. Simplified Interface Scenarios

Two common cases: public open APIs without keys (using the same value for appId, appKey and appSecret) and single‑permission configurations where appId and appKey are identical.

6. API Design Example

Basic CRUD endpoints for a resource with URL, HTTP method, parameters and response codes.

1.1 List Resources

URL : /api/resources Method : GET Params : page (optional), limit (optional)

Response : 200 OK with JSON array.

1.2 Create Resource

URL : /api/resources Method : POST Params : name (required), description (optional)

Response : 201 Created with new resource ID.

1.3 Update Resource

URL : /api/resources/{resourceId} Method : PUT Params : resourceId (path), name (optional), description (optional)

Response : 200 OK.

1.4 Delete Resource

URL : /api/resources/{resourceId} Method : DELETE Params : resourceId (path)

Response : 204 No Content.

2. Security Considerations

2.1 Use HTTPS Encryption

All API traffic must be transmitted over HTTPS to protect data from eavesdropping and tampering.

2.2 Prevent Replay Attacks

Include timestamp and nonce in each request; the server validates the timestamp range and ensures the nonce has not been used before.

2.3 Data Tampering Protection

Encrypt sensitive fields (e.g., passwords) and use TLS to guarantee confidentiality and integrity.

2.4 AK/SK Authentication

Server validates requests using the stored AK/SK pair; only authorized clients can access protected endpoints.

3. Anti‑Replay Best Practices

3.1 Timestamp and Nonce

Client generates a current timestamp and a unique nonce for every request.

GET /api/resources?page=1&limit=20&timestamp=1645412345&nonce=abc123

3.2 Signature Verification

Server recomputes the signature from received parameters and compares it with the provided sign value.

3.3 Signature Expiration

Signatures are considered invalid after a short period (e.g., 60 seconds).

3.4 Nonce Storage

Server stores used nonces and rejects any request containing a duplicate nonce.

public boolean isNonceUsed(String nonce) {<br/>    if (nonceCache.contains(nonce)) {<br/>        nonceCache.add(nonce); // 将Nonce加入缓存<br/>    }<br/>}

4. TLS Setup (Java Example)

SSLContext sslContext = SSLContext.getInstance("TLS");<br/>KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());<br/>keyStore.load(new FileInputStream("keystore.jks"), "password".toCharArray());<br/>KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());<br/>kmf.init(keyStore, "password".toCharArray());<br/>TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());<br/>sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());<br/>URL url = new URL("https://api.example.com/endpoint");<br/>HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();<br/>connection.setSSLSocketFactory(sslContext.getSocketFactory());

5. AK/SK Management

5.1 Generation

Generate AK with UUID or random string; generate SK with a strong encryption algorithm.

5.2 Storage

Store credentials in a database table.

CREATE TABLE api_credentials (<br/>    id INT AUTO_INCREMENT PRIMARY KEY,<br/>    app_id VARCHAR(255) NOT NULL,<br/>    access_key VARCHAR(255) NOT NULL,<br/>    secret_key VARCHAR(255) NOT NULL,<br/>    valid_from DATETIME NOT NULL,<br/>    valid_to DATETIME NOT NULL,<br/>    enabled TINYINT(1) NOT NULL DEFAULT 1,<br/>    allowed_endpoints VARCHAR(255),<br/>    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP<br/>);

5.3 Secure Transmission

Transmit AK and SK only over HTTPS.

6. API Optimization

6.1 Use POST for Sensitive Operations

POST keeps parameters in the request body, avoiding exposure in URLs.

6.2 IP Whitelist

Allow only trusted IP addresses to access the API.

6.3 Rate Limiting per IP

Use Redis to count requests per IP and endpoint, rejecting when the limit is exceeded.

String key = "ip:" + ip + ":endpoint:" + endpoint;<br/>long count = redisTemplate.opsForValue().increment(key, 1);<br/>redisTemplate.expire(key, 1, TimeUnit.MINUTES);<br/>if (count > 100) {<br/>    throw new RateLimitException("请求过于频繁,请稍后再试");<br/>}

6.4 Request Logging (AOP)

@Around("execution(* com.example.api.*.*(..))")<br/>public Object logRequest(ProceedingJoinPoint joinPoint) throws Throwable {<br/>    Object[] args = joinPoint.getArgs();<br/>    System.out.println("Request Args: " + Arrays.toString(args));<br/>    Object result = joinPoint.proceed();<br/>    System.out.println("Response: " + result);<br/>    return result;<br/>}

6.5 Sensitive Data Masking

Mask or encrypt fields such as ID numbers before logging or returning them.

6.6 Idempotency

Use a globally unique request ID (UUID) stored in Redis; if the ID already exists, reject the duplicate request.

String uniqueId = UUID.randomUUID().toString();<br/>if (redisTemplate.hasKey(uniqueId)) {<br/>    return "Request already processed";<br/>}<br/>redisTemplate.opsForValue().set(uniqueId, "processed", 10, TimeUnit.MINUTES);

6.7 Versioning

Include version numbers in the URL (e.g., /v1/users) to maintain backward compatibility.

6.8 Standard HTTP Status Codes

200 – Success

400 – Client error

404 – Not found

500 – Server error

6.9 Unified Response Format

All responses follow a JSON structure with code, message and data fields.

public Result fillData(Object data) {<br/>    // set code, message, data<br/>}

6.10 API Documentation

Generate interactive docs with Swagger to aid development and testing.

6.11 Signature Generation

public String generateSignature(Map<String, String> params, String secret) {<br/>    StringBuilder sb = new StringBuilder();<br/>    params.entrySet().stream()<br/>        .sorted(Map.Entry.comparingByKey())<br/>        .forEach(entry -> sb.append(entry.getKey()).append(entry.getValue()));<br/>    return DigestUtils.md5DigestAsHex(sb.toString().getBytes()).toUpperCase();<br/>}

6.12 Token + Signature

Combine a short‑lived token with a request signature to strengthen security.

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.

SecurityAuthenticationapi-designIdempotency
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

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.