【问题标题】:Retrieving the request body in a tower layer to sign GRPC requests在塔层中检索请求主体以签署 GRPC 请求
【发布时间】:2021-09-11 13:35:43
【问题描述】:

我正在尝试在 grpc 之上实现一个身份验证层(通过 tonic),它具有塔式中间件层功能。为此,我需要获取请求的主体,包括发送到服务器的 protobuf 有效负载,使用 HMAC 对其进行身份验证,然后在元数据/标头中设置身份验证 HMAC。

但是,我在通过 API 检索请求正文时遇到了一些问题,这似乎没有等待整个请求被概括为小型和大型流式请求。后者在实际向服务器发出请求之前需要在内存中缓冲一个大的主体。由于我可以控制要拦截的请求,并且我知道我的请求足够小,可以在内存中缓冲而没有太多开销,因此这种概括增加了一些我不知道如何处理的复杂性。

我的图层当前如下所示:

use http::Request;
use std::task::{Context, Poll};
use tonic::body::BoxBody;
use tonic::codegen::Body;
use tower::{Layer, Service};

pub struct AuthLayer {
    hmac_key: Vec<u8>,
}

impl AuthLayer {
    pub fn new(hmac_key: Vec<u8>) -> Self {
        AuthLayer { hmac_key: hmac_key }
    }
}

impl<S> Layer<S> for AuthLayer {
    type Service = AuthService<S>;

    fn layer(&self, inner: S) -> Self::Service {
        AuthService {
            hmac_key: self.hmac_key.clone(),
            inner,
        }
    }
}

// This service implements the Log behavior
pub struct AuthService<S> {
    hmac_key: Vec<u8>,
    inner: S,
}

impl<S> Service<Request<BoxBody>> for AuthService<S>
where
    S: Service<Request<BoxBody>>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = S::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx)
    }

    fn call(&mut self, request: Request<BoxBody>) -> Self::Future {
        let _body = dbg!(request.body().data());
        // TODO Compute an HMAC over _body
        // TODO Set HMAC in metadata / headers
        self.inner.call(request)
    }
}

它失败是因为request 不可变,因此我们不能在主体上调用data()。由于请求很小,我可以克隆整个请求,缓冲正文,然后在其上计算 HMAC,然后将请求转发到 inner 服务,但我尝试的一切都失败了。但是,理想情况下,可以就地缓冲并转发原件。

如何从http::Request 获取正文?

【问题讨论】:

  • let mut request = request; let request_body = request.body_mut(); let body = request_body.data(); 怎么样?
  • 非常有趣,这实际上可以,我得到一个Data&lt;BoxBody&lt;Bytes, Status&gt;&gt; 好吧,这正是我想要的。我很惊讶这行得通,但我并不完全理解它。我认为我不能只更改 call() 的签名,因为它来自一个特征,但是如何将非 mut 借用升级为 mut 借用安全?
  • 我已经发布了答案
  • 太好了,非常感谢,这让我解脱了^^

标签: rust middleware rust-tokio rust-tonic


【解决方案1】:

方法签名按值接受请求:

    fn call(&mut self, request: Request<BoxBody>) -> Self::Future 

因此,您可以重新绑定它以使其可变。通过重新绑定它,您将数据移动到一个新变量(同名),它是可变的:

let mut request = request; 
let request_body = request.body_mut(); 
let body = request_body.data();

您也可以直接在方法上添加mut

    fn call(&mut self, mut request: Request<BoxBody>) -> Self::Future 

这不会改变签名,因为调用者并不关心你是否会修改它,因为它是按值传递的。

如何将非 mut 借用升级为 mut 借用安全?

它是安全的(并且可能),因为它是按值传递的,而不是按引用传递的。作为该值的唯一所有者,您可以使其可变或不可变

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    • 2021-11-03
    • 2019-03-25
    • 2014-05-17
    • 1970-01-01
    • 2019-03-10
    相关资源
    最近更新 更多