我正在尝试创建一个中间件来使用 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 的任何帮助。我在这方面陷入了困境。
有同样的问题。
通过从 actix 中间件示例推断来解决。
使用下面的链接访问示例。
https://github.com/actix/examples/blob/master/middleware/middleware/src/redirect.rs