0%

actix-web 中间件教程(1)

使用中间件进行认证

依赖

1
2
3
4
5
6
7
8
9
10
11
[package]
name = "day25"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4.11.0"
dotenv = "0.15.0"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.1"
futures = "0.3.31"

main.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde_json::json;

use crate::auth_middleware::Authentication;

mod auth_middleware;

//无需认证
async fn anonymous() -> impl Responder {
HttpResponse::Ok().json(json!({
"code": 1,
"message":"请求完成!",
"data": "anonymous"
}))
}

//需要认证
async fn mock_user() -> impl Responder {
HttpResponse::Ok().json(json!({
"code": 1,
"message":"请求完成!",
"data": ["张三", "李四", "王五"]
}))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {

dotenv::dotenv().ok();

HttpServer::new(|| {
App::new()
.wrap(Authentication)
.route("/anonymous", web::get().to(anonymous))
.route("/mockUser", web::get().to(mock_user))
})
.bind("127.0.0.1:8080")?
.run()
.await
}

auth_middleware.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use actix_web::{
body::EitherBody,
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
Error, HttpResponse,
};
use futures::future::{ok, LocalBoxFuture, Ready};
use serde_json::json;

pub struct Authentication;
impl<S, B> Transform<S, ServiceRequest> for Authentication
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type InitError = ();
type Transform = AuthenticationMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;

fn new_transform(&self, service: S) -> Self::Future {
ok(AuthenticationMiddleware { service })
}
}
pub struct AuthenticationMiddleware<S> {
service: S,
}

impl<S, B> Service<ServiceRequest> for AuthenticationMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

forward_ready!(service);

fn call(&self, req: ServiceRequest) -> Self::Future {
println!("Hi from start. You requested: {}", req.path());

//当前请求路径
let path = req.path().to_string();

//白名单
let whitelist: Vec<String> = std::env::var("WL_LIST")
.unwrap_or_default()
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();

//获取 api-key
let token = req
.headers()
.get("api-key")
.and_then(|v| v.to_str().ok())
.unwrap_or("");

let authenticate_passbool;
//判断白名单
if whitelist.iter().any(|p| path.starts_with(p)) {
//白名单放行
authenticate_passbool = true;
} else {
//验证token
if token == "08a31351f3324664a4e982310e76583b" {
//正确
authenticate_passbool = true;
} else {
authenticate_passbool = false;
}
}

if authenticate_passbool {
//放行
let res = self.service.call(req);
return Box::pin(async move { res.await.map(ServiceResponse::map_into_left_body) });
} else {
//失败
let (request, _pl) = req.into_parts();
let response: HttpResponse<actix_web::body::EitherBody<_>> =
HttpResponse::Unauthorized()
.json(json!({
"code": 401,
"message": "Unauthorized",
"data": null
}))
.map_into_right_body();
return Box::pin(async { Ok(ServiceResponse::new(request, response)) });
}
}
}

.env

1
2
#不需要认证的uri
WL_LIST=/anonymous,/public

预览

匿名

匿名

认证成功

认证成功

认证失败

认证失败

其他说明

EitherBody<A, B> 是一个枚举体,它允许你用一种统一的方式返回两种不同类型的响应体(body)

1
2
3
4
pub enum EitherBody<A, B> {
Left(A),
Right(B),
}

Left(B) 表示正常响应。
Right(BoxBody) 通常是错误或自定义响应。