Skip to content

源码:labs/java/backend-data/auth-token-simulation/src/AuthTokenSimulation.java ​

  • 原始路径:labs/java/backend-data/auth-token-simulation/src/AuthTokenSimulation.java
  • 类型:java
java
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * Token 鉴权仿真:JWT 结构 + 双 Token 无感刷新 + 并发 401 排队。
 *
 * 对应 docs 06-backend-data/06-auth-token-and-oauth2.md:
 * 1. JWT 三段式(header.payload.signature):Header/Payload 只是 Base64,防篡改不防读取
 * 2. 双 Token:access_token 短命(15min)+ refresh_token 长命(30d)
 * 3. 并发 401 排队:多个请求同时 401,只刷新一次 token,其余复用新 token 重放
 *
 * 纯 Java CLI(无外部依赖,手写 Base64 演示 JWT 结构)。
 * 运行:javac -d out src/AuthTokenSimulation.java && java -cp out AuthTokenSimulation
 */
public class AuthTokenSimulation {

    public static void main(String[] args) {
        System.out.println("========== 1. JWT 三段式结构(防篡改不防读取)==========");
        String jwt = buildJwt("user_10086", "alex", "viewer");
        String[] parts = jwt.split("\\.");
        System.out.println("  JWT: " + jwt);
        System.out.println("  Header  (Base64 解码): " + decode(parts[0]));
        System.out.println("  Payload (Base64 解码): " + decode(parts[1]));
        System.out.println("  Signature: 对 header.payload 签名,任何人可读但不可改(改了就验签失败)");

        System.out.println();
        System.out.println("========== 2. 双 Token 生命周期 ==========");
        System.out.println("  access_token:  15 分钟,请求头 Authorization: Bearer <access>");
        System.out.println("  refresh_token: 30 天,仅用于换新 access_token(绝不进请求头)");

        System.out.println();
        System.out.println("========== 3. 并发 401 排队:只刷新一次 ==========");
        // 模拟 10 个并发请求在 access_token 过期瞬间同时收到 401
        int concurrent = 10;
        int refreshCount = 0;
        boolean tokenRefreshed = false;
        for (int i = 0; i < concurrent; i++) {
            if (!tokenRefreshed) {
                synchronized (AuthTokenSimulation.class) {
                    if (!tokenRefreshed) {           // Double-Check:只有第一个请求进临界区刷新
                        refreshCount++;
                        tokenRefreshed = true;       // 模拟换到新 token
                    }
                }
            }
            // 其余请求复用新 token 重放,不再刷新
        }
        System.out.println("  " + concurrent + " 个并发 401 请求,实际触发刷新次数 = " + refreshCount + "(其余复用新 token 重放)");

        System.out.println();
        System.out.println("========== 4. 结论 ==========");
        System.out.println("  · JWT 内容任何人可读:Payload 只放非敏感标识,不放密码/手机号");
        System.out.println("  · 双 Token 刷新必须串行化(OkHttp Authenticator / 并发锁),否则 401 风暴");
        System.out.println("  · refresh_token 也失效时:静默跳登录页,避免死循环重试");
    }

    /** 构造 JWT(简化:签名用"伪签名"示意,真实是 HMAC/RSA) */
    static String buildJwt(String userId, String name, String role) {
        String header = encode("{\"alg\":\"HS256\",\"typ\":\"JWT\"}");
        String payload = encode("{\"sub\":\"" + userId + "\",\"name\":\"" + name
                + "\",\"role\":\"" + role + "\",\"exp\":1770000000}");
        String signature = "fake_signature_for_demo"; // 真实:HMAC-SHA256(header.payload, secret)
        return header + "." + payload + "." + signature;
    }

    static String encode(String s) {
        return Base64.getUrlEncoder().withoutPadding()
                .encodeToString(s.getBytes(StandardCharsets.UTF_8));
    }

    static String decode(String s) {
        return new String(Base64.getUrlDecoder().decode(s), StandardCharsets.UTF_8);
    }
}

站点构建时间:2026/8/24 23:43:17