拦截服务器和客户端中的gRPC C ++调用 [英] Intercept gRPC C++ calls in server and client
问题描述
我要执行的基本任务:在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 $ c而言不是必需的) $ c>方法,但对于各种随后调用的方法都是必需的)。如问题: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屋!