RPC 调用如何保证安全性?认证、加密与授权怎么做?
RPC 调用走的是网络,任何中间人都能截获、篡改甚至伪造请求,所以安全性不是可选项,而是必选项。核心要解决三个问题:你是谁(认证)、数据别被偷看(加密)、你能干什么(授权)。
身份认证:确认调用方身份
最常见的是 Token 认证,客户端每次请求携带 JWT 或 OAuth2 Token,服务端校验后放行。gRPC 中通常用拦截器统一拦截:
javapublic class AuthInterceptor implements ServerInterceptor { @Override public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall( ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { String token = headers.get(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER)); if (!validateToken(token)) { call.close(Status.UNAUTHENTICATED.withDescription("Invalid token"), headers); return new ServerCall.Listener<ReqT>() {}; } return next.startCall(call, headers); } }
内部服务间调用更简单的做法是 API Key,给每个服务分配固定密钥,缺点是一旦泄露很难快速更换。
安全性要求高的场景用双向 TLS(mTLS):客户端和服务端互相验证证书,只有持有合法证书的才能通信,即使 Token 被盗也无法伪造连接。
数据加密:防止传输中被窃听和篡改
传输层加密是第一道防线。所有 RPC 框架都支持 TLS,gRPC 配置示例:
javaNettyChannelBuilder.forAddress(host, port) .sslContext(GrpcSslContexts.forClient() .trustManager(new File("ca.pem")) .build()) .build();
仅靠 TLS 不够,敏感字段还应做应用层加密(AES/RSA),这样即便 TLS 被中间人攻破,核心数据仍有保护。
完整性校验用 HMAC 或数字签名。发送方对请求体计算签名,接收方验证签名是否一致,能发现任何篡改行为。
授权:控制调用方能访问什么
认证通过后还要判断有没有权限。最实用的是 RBAC,给服务或用户分配角色,角色绑定权限集合。也可以做到方法级别的细粒度控制,比如只有 admin 角色才能调用 DeleteUser。
实际项目中通常用注解 + 拦截器的方式:
java@RequireRole("admin") public void deleteUser(UserRequest req) { ... }
拦截器在调用前统一校验角色,业务代码无需关心权限逻辑。
防重放和限流
重放攻击是拿合法请求重复发送。解法是请求中加时间戳 + Nonce(一次性随机数),服务端校验时间窗口内的请求是否重复。再加上请求签名,把时间戳、Nonce 和参数一起签名,篡改任何一项都会验签失败。
限流用令牌桶算法,防止某个调用方吃满资源:
javaRateLimiter rateLimiter = RateLimiter.create(100); if (!rateLimiter.tryAcquire()) { throw new RateLimitExceededException(); }
配合 IP 黑白名单,可疑来源直接拦截。
安全实践要点
- 最小权限:服务只开通必需的调用权限
- 密钥轮换:定期更换 Token、证书,用配置中心管理而非硬编码
- 审计日志:记录调用方、时间、参数,异常模式及时告警
- 框架差异:gRPC 原生支持 TLS + 拦截器;Dubbo 用 Filter 扩展;Thrift 用 TSSLTransport
追问:mTLS 和普通 TLS 的区别? 普通 TLS 只验证服务端证书,客户端不提供证书;mTLS 要求双方都提供证书互相验证,安全性更高但证书管理成本也更大,适合零信任网络架构。