5月27日 23:23

RPC 调用如何保证安全性?认证、加密与授权怎么做?

RPC 调用走的是网络,任何中间人都能截获、篡改甚至伪造请求,所以安全性不是可选项,而是必选项。核心要解决三个问题:你是谁(认证)、数据别被偷看(加密)、你能干什么(授权)。

身份认证:确认调用方身份

最常见的是 Token 认证,客户端每次请求携带 JWT 或 OAuth2 Token,服务端校验后放行。gRPC 中通常用拦截器统一拦截:

java
public 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 配置示例:

java
NettyChannelBuilder.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 和参数一起签名,篡改任何一项都会验签失败。

限流用令牌桶算法,防止某个调用方吃满资源:

java
RateLimiter rateLimiter = RateLimiter.create(100); if (!rateLimiter.tryAcquire()) { throw new RateLimitExceededException(); }

配合 IP 黑白名单,可疑来源直接拦截。

安全实践要点

  • 最小权限:服务只开通必需的调用权限
  • 密钥轮换:定期更换 Token、证书,用配置中心管理而非硬编码
  • 审计日志:记录调用方、时间、参数,异常模式及时告警
  • 框架差异:gRPC 原生支持 TLS + 拦截器;Dubbo 用 Filter 扩展;Thrift 用 TSSLTransport

追问:mTLS 和普通 TLS 的区别? 普通 TLS 只验证服务端证书,客户端不提供证书;mTLS 要求双方都提供证书互相验证,安全性更高但证书管理成本也更大,适合零信任网络架构。

标签:RPC