gRPC 的 token 认证是怎么挂到每个调用上的?为什么必须配 TLS?token 要过期了怎么刷新才不出错?并发刷新会不会把令牌服务打爆?这篇用自己实现的 PerRPCCredentials 逐项验证。
RequireTransportSecurity()=true + 明文连接:NewClient 的结果:grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set) RequireTransportSecurity()=false + 明文连接:代理(窃听者)在线上看到 authorization: Bearer SECRET-TOKEN-123 RequireTransportSecurity()=true + TLS 连接:NewClient 结果 <nil>;调用 成功
PerRPCCredentials 有两个方法:GetRequestMetadata(每个 RPC 调用一次,返回要加的头)和 RequireTransportSecurity(是否必须加密)。用 WithPerRPCCredentials 设为连接级,或用 PerRPCCredentials 调用选项设单次。
100 个调用:GetRequestMetadata 被调用 100 次;请求 HEADERS 帧载荷大小 第 1 个 716 字节、第 2 个 17 字节、第 100 个 9 字节
声明需要加密而连接是明文,NewClient 直接报错;如果声明不需要,令牌会在线上以明文出现。
每次都取新令牌 共 141 次调用;取令牌 141 次;被服务端拒绝 0 次(0.0%) 缓存,过期才刷新 共 141 次调用;取令牌 3 次;被服务端拒绝 13 次(9.2%) 缓存,提前 300 ms 刷新 共 142 次调用;取令牌 5 次;被服务端拒绝 0 次(0.0%)
令牌过期前就刷新,留出时钟偏差和传输延迟的余量,否则会出现一批“刚好过期”的请求被拒绝。
不加锁 50 个并发调用,取令牌 50 次 加锁(只有一个去取,其余等待) 50 个并发调用,取令牌 1 次
GetRequestMetadata 会被并发调用,刷新令牌要有锁,避免过期瞬间所有调用一起去取。
GetRequestMetadata 返回 普通 error → 调用 code=Unauthenticated message="transport: per-RPC creds failed due to error: 取令牌失败" GetRequestMetadata 返回 status.Error(Unavailable) → 调用 code=Unavailable message="令牌服务不可用" GetRequestMetadata 返回 status.Error(PermissionDenied) → 调用 code=PermissionDenied message="无权" 取令牌要 500 ms、调用 deadline 200 ms:code=Unauthenticated message="transport: per-RPC creds failed due to error: context deadline exceeded";201.2 ms 后返回
返回 status 错误能控制调用得到的 code;返回普通 error 会变成 Unauthenticated。取令牌的时间算在调用的 deadline 里。
只有连接级凭证:服务端看到 authorization = ["Bearer CONN-LEVEL"] 再加单次调用的凭证:服务端看到 authorization = ["Bearer CONN-LEVEL" "Bearer CALL-LEVEL"]
连接级和单次调用的凭证同时存在时,两个 authorization 都会被发送。