使用actix_session与RedisActorSessionStore跨路由持久化数据遇阻
问题:Actix Web会话无法跨路由持久化,Redis中已存数据但其他路由无法读取
将会话数据写入Redis后,无法在其他路由中检索到这些数据。登录路由中插入会话数据后,能通过session.entries()或session.get::<>()立即查看数据,但其他路由中会话始终为空,推测会话无法跨路由持久化。我对浏览器端会话存储机制了解有限,只知道它是安全存储且随HTTP请求自动发送,难以判断问题出在会话数据发送到浏览器的环节,还是从浏览器检索会话的环节。
登录路由代码
#[post("/login")] async fn login(session: Session, user_info: web::Json<LoginDetails>) -> impl Responder { ... // 密码验证逻辑 ... session.insert("user_id".to_string(), user_id); session.insert("status_level".to_string(), status_level); session.insert("login_status".to_string(), true); let user_session = UserSession { session_string: session_string.to_string(), session_details }; dbg!(session.entries()); // 这里能正常显示会话条目 :) dbg!(session.get::<u8>("user_id")); return HttpResponse::Ok().status(StatusCode::from_u16(200).unwrap()).json(user_session); }, ...
会话验证函数
pub fn validate_session(session: &Session) -> Result<u8, HttpResponse> { let user_id: Option<u8> = session.get("user_id").unwrap_or_else(|err| { println!("Error getting user_id from session: {}", err); None }); dbg!(session.entries()); // 这里会话为空 :( dbg!(&user_id); match user_id { Some(id) => { // 延长会话有效期 session.renew(); Ok(id) } None => Err(HttpResponse::Unauthorized().json("Unauthorized")), } }
受会话保护的路由
#[get("/campaign_names")] async fn campaign_names(session: Session, req: HttpRequest) -> impl Responder { let id = validate_session(&session).unwrap_or(0); println!("id {}",id); // 输出 0 ... }
主入口路由配置
#[actix_web::main] async fn main() -> std::io::Result<()> { let secret_key = Key::generate(); HttpServer::new(move || { let cors = Cors::permissive(); App::new() .wrap(cors) .wrap( SessionMiddleware::builder( RedisActorSessionStore::new("127.0.0.1:6379"), secret_key.clone(), ) // 允许JavaScript访问Cookie .cookie_http_only(false) // 仅允许当前域名使用Cookie .cookie_same_site(SameSite::Strict) .build(), ) .service(campaign_names) ... }
补充信息
- 已确认Redis中已写入会话数据,尝试过非Actor会话存储,问题依旧。
- 参考过官方认证示例,推测会话ID的发送与返回应该由中间件自动处理,但实际未生效。
解决方案
1. 修复跨域请求的凭证携带问题
如果前端与后端是跨域部署,必须让前端请求显式携带凭证:
- Axios:设置
withCredentials: trueaxios.get('/campaign_names', { withCredentials: true }) - Fetch API:设置
credentials: 'include'fetch('/campaign_names', { credentials: 'include' })
浏览器默认不会在跨域请求中携带Cookie,即使你设置了Cors::permissive(),也必须开启这个配置。
2. 检查会话Cookie的响应头设置
登录请求完成后,查看响应头中的Set-Cookie字段,确认:
- Cookie的
Domain设置正确,与前端域名匹配(如果跨域) - 若使用
SameSite::Strict,需确保请求是同域发起;跨域场景可改为SameSite::Lax,如果是HTTPS环境可尝试SameSite::None(需配合cookie_secure(true))
3. 确保会话数据及时提交到Redis
在登录路由中显式调用session.commit().await,强制将会话数据写入Redis:
// 插入会话数据后添加 session.commit().await?;
虽然Actix默认会在响应发送时自动提交,但显式调用可以避免异步场景下的提交延迟。
4. 验证Redis连接与会话数据
- 登录后在Redis中执行
KEYS *,查看是否存在会话键(通常格式为actix-session:<会话ID>) - 使用
GET <会话键>查看存储的内容,确认user_id等字段是否存在
5. 固定会话密钥
不要每次服务重启都生成新的secret_key,否则旧会话的签名会失效,导致无法读取。可以将密钥存储在配置文件中,每次启动加载:
// 从配置文件读取密钥,示例: let secret_key = Key::from(base64::decode("你的固定密钥").unwrap());
内容的提问来源于stack exchange,提问作者Garrett Berg
相关产品推荐
相关产品推荐

