拦截服务器和客户端中的gRPC C ++调用 [英] Intercept gRPC C++ calls in server and client

查看:189
本文介绍了拦截服务器和客户端中的gRPC C ++调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Basic task I want to do: Provide a Authenticate service in gRPC server that all clients call (and supply user name and password) initially to obtain a authorization token (say JWT). Next, when other service calls are made by the client, the token should be verified.

可以使用 ServerInterceptor ClientInterceptor 接口。在 ServerInterceptor 中,我可以检查调用了哪个服务并确定是允许还是拒绝该调用。在 ClientInterceptor 端,我可以将授权令牌作为元数据添加到每个服务调用中。

This can be accomplished in Java APIs easily using ServerInterceptor and ClientInterceptor interfaces. In ServerInterceptor I can check which service is called and decide whether to allow or deny the call. On the ClientInterceptor side I can add the authorization token as metadata to every service call.

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

There is this AuthMetadataProcessor abstract class in C++. But not sure how to accomplish the task similar to Java APIs. Is there a way to do similar things in C++ APIs ?

推荐答案

是的。您需要为 AuthMetadataProcessor 子类化,重写其 Process 方法,并在服务中注册派生类型的实例。完成此操作后,所有方法调用将被 Process 拦截,并将为该请求提供与请求一起发送的客户端元数据。

Yes. You need to subclass AuthMetadataProcessor, override its Process method and register an instance of the derived type with your service. Once that is done, all method invocations will be intercepted by Process and it will be given the client metadata sent with the request.

您的 Process 的实现必须决定是否需要对所拦截的方法进行身份验证(即,对于您的 Authenticate 方法,但对于各种随后调用的方法都是必需的)。如问题:path 元数据键来完成此操作。 rel = noreferrer>#9211 ,它是一个可信任的值,用于指定被拦截的方法。

Your implementation of Process must decide whether authentication is required for the intercepted method (i.e., cannot be required for your Authenticate method, but will be required for various subsequently invoked methods). This can be done by examining the :path metadata key, as documented in issue #9211, which is a trusted value designating the intercepted method.

您对 Process的实现必须确定令牌是否在请求中提供并有效。这是一个实现细节,但通常 Process 是指由 Authenticate 生成的有效令牌的存储。

Your implementation of Process must decide whether the token is supplied in the request and is valid. This is an implementation detail, but generally Process refers to a store of valid tokens generated by Authenticate. Which is probably how you have it set up in Java already.

不幸的是,您不能在不安全的凭据上注册AuthMetadataProcessor,这意味着您将必须使用SSL,

Unfortunately, one cannot register an AuthMetadataProcessor on top of insecure credentials, meaning that you will have to use SSL, or else attempt to intercept methods differently.

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

The framework also provides convenience functionality allowing you to work with a peer identity property. Process can call AddProperty on the authentication context, providing the identity implied by the token, followed by SetPeerIdentityPropertyName. The invoked method can then access the information using GetPeerIdentity and avoid remapping tokens to identities.

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 C ++调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆