You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Armeria集成gRPC Java如何实现拦截器并正确操作Context

Armeria 集成 gRPC 实现JWT鉴权的Context操作实现方案

核心实现逻辑

你采用装饰器模式包裹gRPC服务做鉴权的思路是可行的,Armeria框架下不推荐直接操作原生gRPC的Context对象,优先使用框架自带的ServiceRequestContext做请求维度的上下文存储,该对象和请求生命周期完全绑定,自动支持线程切换、异步任务的上下文传播,不会出现上下文丢失问题,适配所有Armeria生态组件。


分步实现

1. 定义上下文属性Key

首先定义全局唯一的属性Key,用于在Context中存取鉴权后的数据,避免和框架内置属性、其他自定义属性冲突:

public final class AuthAttrKeys {
    // 泛型指定你要存储的鉴权结果类型,比如用户信息、权限列表等
    public static final AttributeKey<UserAuthInfo> USER_AUTH = 
        AttributeKey.valueOf(AuthAttrKeys.class, "USER_AUTH_INFO");
}

2. 实现JWT鉴权装饰器

自定义装饰器实现请求拦截、JWT校验、上下文写入、请求放行的全逻辑:

public class JwtAuthDecorator implements DecoratingHttpServiceFunction {
    private final JwtTokenValidator tokenValidator; // 自行实现的JWT校验工具

    @Override
    public HttpResponse serve(HttpService delegate, ServiceRequestContext ctx, HttpRequest req) throws Exception {
        // 从请求头获取Authorization字段
        final String authHeader = req.headers().get(HttpHeaderNames.AUTHORIZATION);
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            // 校验不通过直接返回gRPC标准未鉴权错误
            return GrpcStatus.UNAUTHENTICATED
                    .withDescription("缺失或非法的Authorization请求头")
                    .asHttpResponse();
        }

        final String token = authHeader.substring(7);
        final UserAuthInfo authInfo;
        try {
            // 执行JWT合法性、有效期、权限范围校验
            authInfo = tokenValidator.validate(token);
        } catch (JwtValidateException e) {
            return GrpcStatus.UNAUTHENTICATED
                    .withDescription("Token校验失败: " + e.getMessage())
                    .asHttpResponse();
        }

        // 校验通过,将鉴权结果写入当前请求的Context
        ctx.setAttr(AuthAttrKeys.USER_AUTH, authInfo);

        // 放行请求到下游实际gRPC服务
        return delegate.serve(ctx, req);
    }
}

3. 注册装饰器到gRPC服务

构建gRPC服务时挂载你实现的鉴权装饰器,无需修改现有业务gRPC服务的代码:

GrpcService businessGrpcService = GrpcService.builder()
        .addService(new UserServiceImpl(), new OrderServiceImpl()) // 你的业务gRPC服务实现
        // 挂载鉴权装饰器,所有挂载的gRPC接口都会经过鉴权逻辑
        .decorate(JwtAuthDecorator.class, () -> new JwtAuthDecorator(jwtTokenValidator))
        .build();

// 将gRPC服务绑定到Armeria Server即可
Server server = Server.builder()
        .http(8080)
        .service(businessGrpcService)
        .build();

4. 业务逻辑中读取上下文数据

在任意gRPC业务方法中,不需要手动传参,直接获取当前请求绑定的Context即可读取之前写入的鉴权数据:

@Override
public void getUserInfo(GetUserInfoReq request, StreamObserver<GetUserInfoResp> responseObserver) {
    // 直接获取当前请求上下文,同步/异步场景都可安全调用
    ServiceRequestContext ctx = ServiceRequestContext.current();
    UserAuthInfo authInfo = ctx.attr(AuthAttrKeys.USER_AUTH);
    
    // 直接使用authInfo做后续业务处理,比如鉴权判断、获取用户ID等
    Long userId = authInfo.getUserId();
    // ... 业务逻辑
}

注意事项

  • 禁止混用原生gRPC Context作为主存储:Armeria虽然兼容原生gRPC Context的读写,但原生Context需要手动处理异步场景的传播,容易出现上下文丢失问题,仅当你需要兼容依赖原生gRPC Context的老组件时才考虑使用
  • 无需手动做上下文的attach/detach操作:Armeria会自动将ServiceRequestContext传播到当前请求派生的所有线程、CompletableFuture、虚拟线程等异步执行单元,直接调用ServiceRequestContext.current()即可获取
  • 如果你需要对部分gRPC方法跳过鉴权,可以在装饰器中通过ctx.rpcRequest().method()获取当前调用的gRPC方法全限定名,配置白名单放行即可

内容的提问来源于stack exchange,提问作者Chan Rao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:30:47