actix-web后端CORS策略拦截请求问题排查求助
Actix-Web中Cors::permissive()未生效,部分路由跨域被拦截的问题分析与解决
问题根源
核心问题出在中间件执行顺序和预请求拦截上:
- Actix-Web的中间件按添加顺序执行,你虽然加了
Cors::permissive(),但AuthMiddleware在它之后处理请求。浏览器发送的预请求(OPTIONS)不会携带Authorization头,AuthMiddleware会因为缺少token直接返回错误响应,此时Cors中间件还没来得及添加跨域头,导致浏览器报错。 - 登录/注册路由不需要经过
AuthMiddleware验证(或你的中间件对这两个路由做了放行),所以它们的预请求能正常被Cors处理,返回正确的跨域头,因此可以正常访问。
解决方案
方案1:调整中间件顺序,让Cors最先执行
把Cors中间件放在所有中间件最前面,确保所有请求(包括预请求)先经过跨域处理,再进入其他中间件:
#[actix_web::main] async fn main() -> std::io::Result<()> { dotenv().ok(); std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); let pool: Pool<Postgres> = PgPoolOptions::new() .max_connections(5) .connect(&std::env::var("LOCAL_DATABASE_URL").expect("DATABASE_URL must be set")) .await .expect("Error building a connection pool"); HttpServer::new(move || { App::new() .wrap(Cors::permissive()) // 移到最前面 .wrap(Logger::default()) .wrap(AuthMiddleware) .app_data(Data::new(AppState{db:pool.clone()})) .route("/signup", web::post().to(signup)) .route("/login", web::post().to(login)) .route("/recruit", web::post().to(recruit)) .route("/get_recruiter", web::post().to(recruiter)) }) .bind("127.0.0.1:8080")? .workers(10) .run() .await }
方案2:让AuthMiddleware跳过OPTIONS请求
如果不想调整中间件顺序,可修改AuthMiddleware,直接放行预请求(OPTIONS)——这类请求不需要token验证:
// 假设你的AuthMiddleware实现如下,添加OPTIONS请求判断 impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>, S::Future: 'static, B: 'static, { type Response = ServiceResponse<B>; type Error = Error; type Transform = AuthMiddlewareService<S>; type InitError = (); type Future = Ready<Result<Self::Transform, Self::InitError>>; fn new_transform(&self, service: S) -> Self::Future { ok(AuthMiddlewareService { service }) } } pub struct AuthMiddlewareService<S> { service: S, } impl<S, B> Service<ServiceRequest> for AuthMiddlewareService<S> where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>, S::Future: 'static, B: 'static, { type Response = ServiceResponse<B>; type Error = Error; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { self.service.poll_ready(cx) } fn call(&self, req: ServiceRequest) -> Self::Future { // 直接放行OPTIONS预请求 if req.method() == Method::OPTIONS { return Box::pin(async move { self.service.call(req).await }); } // 你的原有token验证逻辑... let fut = self.service.call(req); Box::pin(async move { let res = fut.await?; Ok(res) }) } }
验证方法
修改后可以用curl测试预请求是否返回正确的跨域头:
curl -X OPTIONS http://127.0.0.1:8080/get_recruiter -H "Origin: http://10.0.0.77:3000" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: Authorization" -v
如果响应头中包含Access-Control-Allow-Origin: *,说明Cors配置已生效。
内容的提问来源于stack exchange,提问作者Santeau
相关产品推荐
相关产品推荐

