得到预期的`HttpResponse<B>`,在actix Web中间件中找到`HttpResponse`

问题描述 投票:0回答:1

我正在尝试创建一个中间件来使用 actix web 验证 jwt 令牌。这是中间件。这往往会从标头获取承载令牌并对其进行验证,如果验证通过,则将用户结构添加到请求中,否则会抛出错误。

use actix_service::Service;
use actix_web::error::ResponseError;
use actix_web::{
    dev::{ServiceRequest, ServiceResponse},
    Error, HttpMessage, Result,
};
use actix_web::{http, HttpResponse};
use futures::future::{ok, Ready};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::Deserialize;
use std::pin::Pin;
use std::{env, fmt};

// JWT Claims structure
#[derive(Debug, Deserialize)]
struct Claims {
    user_id: String,
    user_name: String,
    exp: usize,
    iat: usize,
    iss: String,
    aud: String,
    sub: String,
    typ: String,
    jti: String,
    role: String,
    permission: String,
}

#[derive(Debug)]
struct UnauthorizedError {
    message: String,
}

impl UnauthorizedError {
    fn new(message: &str) -> Self {
        UnauthorizedError {
            message: message.to_string(),
        }
    }
}

impl ResponseError for UnauthorizedError {
    fn status_code(&self) -> http::StatusCode {
        http::StatusCode::UNAUTHORIZED
    }
}

impl fmt::Display for UnauthorizedError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Unauthorized: {}", self.message)
    }
}

pub struct JwtMiddleware;

impl<S, B> actix_service::Transform<S, ServiceRequest> for JwtMiddleware
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + Clone,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = JwtMiddlewareMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(JwtMiddlewareMiddleware { service })
    }
}

pub struct JwtMiddlewareMiddleware<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for JwtMiddlewareMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + Clone,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn futures::Future<Output = Result<Self::Response, Self::Error>>>>;

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

    fn call(&self, req: ServiceRequest) -> Self::Future {
        // Extract the Authorization header
        let auth_header = req.headers().get("Authorization");

        if let Some(token) = auth_header {
            if let Ok(token_str) = token.to_str() {
                if token_str.starts_with("Bearer ") {
                    // Extract the JWT from the header
                    let jwt = token_str[7..].to_string();
                    let secret_key = env::var("SECRET_KEY").expect("SECRET_KEY not set");
                    let secret = secret_key.as_bytes(); // Replace with your secret key

                    // Decoding key and validation settings
                    let decoding_key = DecodingKey::from_secret(secret);
                    let validation = Validation {
                        algorithms: vec![Algorithm::HS256], // Adjust the algorithm accordingly
                        ..Default::default()
                    };

                    match decode::<Claims>(&jwt, &decoding_key, &validation) {
                        Ok(_) => {
                            // JWT is valid, continue to the inner service
                            let fut = self.service.call(req);
                            Box::pin(async move {
                                let res = fut.await?;
                                Ok::<ServiceResponse<B>, Error>(res)
                            }) as Self::Future
                        }
                        Err(_) => {
                            // JWT is invalid, return unauthorized response
                            let err = UnauthorizedError::new("Access denied");
                            let res = req.error_response(err);
                            Box::pin(async move {
                                let new_response = HttpResponse::Ok().finish(); // Create a new HttpResponse
                                Ok::<ServiceResponse<B>, Error>(ServiceResponse::new(
                                    req.request().clone(),
                                    new_response,
                                ))
                            }) as Self::Future
                        }
                    }
                } else {
                    // Token doesn't start with "Bearer "
                    let err = UnauthorizedError::new("Bad Request");
                    let res = req.error_response(err);
                    Box::pin(async {
                        let new_response = HttpResponse::Ok().finish(); // Create a new HttpResponse
                        Ok::<ServiceResponse<B>, Error>(ServiceResponse::new(
                            req.request().clone(),
                            new_response,
                        ))
                    }) as Self::Future
                }
            } else {
                // Token is not a valid UTF-8 string
                let err = UnauthorizedError::new("Bad Request");
                let res = req.error_response(err);
                Box::pin(async {
                    let new_response = HttpResponse::Ok().finish(); // Create a new HttpResponse
                    Ok::<ServiceResponse<B>, Error>(ServiceResponse::new(
                        req.request().clone(),
                        new_response,
                    ))
                }) as Self::Future
            }
        } else {
            // Token not found in the header
            let err = UnauthorizedError::new("Unauthorized");
            let res = req.error_response(err);
            Box::pin(async {
                let new_response = HttpResponse::Ok().finish(); // Create a new HttpResponse
                Ok::<ServiceResponse<B>, Error>(ServiceResponse::new(
                    req.request().clone(),
                    new_response,
                ))
            }) as Self::Future
        }
    }
}

但是我收到此错误并且无法解决它。

impl<S, B> Service<ServiceRequest> for JwtMiddlewareMiddleware<S>
    |         - this type parameter
...
125 |                                 Ok::<ServiceResponse<B>, Error>(ServiceResponse::new(
    |                                                                 -------------------- arguments to this function are incorrect
126 |                                     req.request().clone(),
127 |                                     new_response,
    |                                     ^^^^^^^^^^^^ expected `HttpResponse<B>`, found `HttpResponse`
    |
    = note: expected struct `HttpResponse<B>`
               found struct `HttpResponse<BoxBody>`
note: associated function defined here

感谢有关将响应类型从 HttpResponse 更改为 HttpResponse 的任何帮助。我在这方面陷入了困境。

rust middleware actix-web
1个回答
0
投票

有同样的问题。

通过从 actix 中间件示例推断来解决。

使用下面的链接访问示例。

https://github.com/actix/examples/blob/master/middleware/middleware/src/redirect.rs

© www.soinside.com 2019 - 2024. All rights reserved.