Intercept gRPC C++ calls in server and client

前端 未结 1 1909
醉梦人生
醉梦人生 2020-12-15 00:59

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 authoriza

相关标签:
1条回答
  • 2020-12-15 01:24

    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.

    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.

    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.

    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.

    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 Implementation Example

    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 Setup within Secure Service

    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());
    };
    
    0 讨论(0)
提交回复
热议问题