【问题标题】:Intercept gRPC C++ calls in server and client在服务器和客户端中拦截 gRPC C++ 调用
【发布时间】:2017-04-24 09:05:17
【问题描述】:

我想做的基本任务:在 gRPC 服务器中提供一个Authenticate 服务,所有客户端最初调用该服务(并提供用户名和密码)以获得授权令牌(比如 JWT)。接下来,当客户端进行其他服务调用时,应验证令牌。

这可以使用ServerInterceptor 和ClientInterceptor 接口在Java API 中轻松完成。在ServerInterceptor 中,我可以检查调用了哪个服务并决定是允许还是拒绝该调用。在ClientInterceptor 方面,我可以将授权令牌作为元数据添加到每个服务调用中。

C++ 中有这个AuthMetadataProcessor 抽象类。但不确定如何完成类似于 Java API 的任务。有没有办法在 C++ API 中做类似的事情?

【问题讨论】:

    标签: c++ grpc


    【解决方案1】:

    是的。您需要继承AuthMetadataProcessor,覆盖其Process 方法,并使用您的服务注册派生类型的实例。完成后,所有方法调用都将被Process 拦截,并将获得随请求发送的客户端元数据。

    Process 的实现必须决定被拦截的方法是否需要身份验证(即,Authenticate 方法不需要身份验证,但随后调用的各种方法都需要身份验证)。这可以通过检查:path 元数据密钥来完成,如问题#9211 中所述,这是指定截获方法的可信值。

    Process 的实现必须决定令牌是否在请求中提供并且是否有效。这是一个实现细节,但通常Process 指的是Authenticate 生成的有效令牌的存储。这可能是您在 Java 中设置它的方式。

    不幸的是,无法在不安全的凭据之上注册 AuthMetadataProcessor,这意味着您将不得不使用 SSL,或者尝试以不同的方式拦截方法。

    该框架还提供了方便的功能,允许您使用对等身份属性。 Process 可以在身份验证上下文上调用AddProperty,提供令牌隐含的身份,然后是SetPeerIdentityPropertyName。然后被调用的方法可以使用GetPeerIdentity 访问信息并避免将令牌重新映射到身份。

    AuthMetadataProcessor 实现示例

    struct Const
    {
        static const std::string& TokenKeyName() { static std::string _("token"); return _; }
        static const std::string& PeerIdentityPropertyName() { static std::string _("username"); return _; }    
    };
    
    class MyServiceAuthProcessor : public grpc::AuthMetadataProcessor
    {
    
    public:
    
        grpc::Status Process(const InputMetadata& auth_metadata, grpc::AuthContext* context, OutputMetadata* consumed_auth_metadata, OutputMetadata* response_metadata) override
        {
            // determine intercepted method
            std::string dispatch_keyname = ":path";
            auto dispatch_kv = auth_metadata.find(dispatch_keyname);
            if (dispatch_kv == auth_metadata.end())
                return grpc::Status(grpc::StatusCode::INTERNAL, "Internal Error");
    
            // if token metadata not necessary, return early, avoid token checking
            auto dispatch_value = std::string(dispatch_kv->second.data());
            if (dispatch_value == "/MyPackage.MyService/Authenticate")
                return grpc::Status::OK;
    
            // determine availability of token metadata
            auto token_kv = auth_metadata.find(Const::TokenKeyName());
            if (token_kv == auth_metadata.end())
                return grpc::Status(grpc::StatusCode::UNAUTHENTICATED, "Missing Token");
    
            // determine validity of token metadata
            auto token_value = std::string(token_kv->second.data());
            if (tokens.count(token_value) == 0)
                return grpc::Status(grpc::StatusCode::UNAUTHENTICATED, "Invalid Token");
    
            // once verified, mark as consumed and store user for later retrieval
            consumed_auth_metadata->insert(std::make_pair(Const::TokenKeyName(), token_value));     // required
            context->AddProperty(Const::PeerIdentityPropertyName(), tokens[token_value]);           // optional
            context->SetPeerIdentityPropertyName(Const::PeerIdentityPropertyName());                // optional
    
            return grpc::Status::OK;
        }
    
        std::map<std::string, std::string> tokens;
    };
    

    安全服务中的 AuthMetadataProcessor 设置

    class MyServiceImplSecure : public MyPackage::MyService::Service
    {
    
    public:
    
        MyServiceImplSecure(std::string _server_priv, std::string _server_cert, std::string _ca_cert) :
            server_priv(_server_priv), server_cert(_server_cert), ca_cert(_ca_cert) {}
    
        std::shared_ptr<grpc::ServerCredentials> GetServerCredentials()
        {
            grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp;
            pkcp.private_key = server_priv;
            pkcp.cert_chain = server_cert;
    
            grpc::SslServerCredentialsOptions ssl_opts;
            ssl_opts.pem_key_cert_pairs.push_back(pkcp);
            ssl_opts.pem_root_certs = ca_cert;
    
            std::shared_ptr<grpc::ServerCredentials> creds = grpc::SslServerCredentials(ssl_opts);
            creds->SetAuthMetadataProcessor(auth_processor);
            return creds;
        }
    
        void GetContextUserMapping(::grpc::ServerContext* context, std::string& username)
        {
            username = context->auth_context()->GetPeerIdentity()[0].data();
        }
    
    private:
    
        std::string server_priv;
        std::string server_cert;
        std::string ca_cert;
    
        std::shared_ptr<MyServiceAuthProcessor> auth_processor =
            std::shared_ptr<MyServiceAuthProcessor>(new MyServiceAuthProcessor());
    };
    

    【讨论】:

    • 谢谢。在浏览了 gRPC 源代码中的一些测试用例后,我也做了类似的事情。
    • 应该是 auto token_value = std::string(token_kv->second.data(), token_kv->second.length());如果有人遇到尾随垃圾字节。
    猜你喜欢
    • 1970-01-01
    • 2019-06-10
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    • 2021-04-10
    • 2020-10-23
    • 2016-04-23
    • 2019-11-24
    相关资源
    最近更新 更多