在服务器和客户端中拦截GRPC C 调用

Intercept gRPC C++ calls in server and client

本文关键字:GRPC 调用 服务器 客户端      更新时间:2023-10-16

我要执行的基本任务:在GRPC服务器中提供Authenticate服务,所有客户端最初都调用(并提供用户名和密码)以获得授权令牌(例如JWT)。接下来,当客户端拨打其他服务调用时,应验证令牌。

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

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

是。您需要子类AuthMetadataProcessor,覆盖其Process方法,然后在服务中注册派生类型的实例。完成此操作后,所有方法调用将被Process拦截,并将为其提供请求的客户端。

您的Process的实现必须决定是否需要拦截方法的身份验证(即,您的Authenticate方法不需要需要身份验证,但随后调用的方法需要进行身份验证)。这可以通过检查:path元数据密钥来完成,如第9211期中的记录,这是指定截距方法的值得信赖的值。

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

不幸的是,一个人无法在不安全凭据之上注册authmetadataprocessor,这意味着您必须使用SSL,或者尝试以不同的方式拦截方法。

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

AUTHMETMETADATAPROCESSOR实现示例

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());
};