Laravel9/Angular13 SPA Sanctum认证报No Session错误如何解决
问题描述
- 运行环境:Angular 13 单页应用 + Laravel 9 后端 + Sanctum 认证体系
- 基础现象:可正常获取 CSRF Cookie 并完成登录认证流程,但所有受保护 API 接口请求均返回
No Session错误;初期排查发现 Angular 侧读取的会话 Cookie 与 Laravel 服务端生成的不匹配,尝试多组SESSION_DOMAIN、SANCTUM_STATEFUL_DOMAINS配置均未解决;注释掉这两个配置项后,前后端 Cookie 值可匹配,但请求受保护接口仍返回 401 响应,提示message: "Unauthenticated." - 现有架构:Angular 侧对
/sanctum、/api路径做代理转发,两类接口均通过localhost:4200域名访问 - 已尝试的 .env 配置如下:
APP_URL=https://[assigned alias].ngrok.io SESSION_DOMAIN=[assigned alias].ngrok.io SANCTUM_STATEFUL_DOMAINS=[assigned alias].ngrok.io APP_URL=https://[alias].tunnelto.dev SESSION_DOMAIN=.tunnelto.dev SANCTUM_STATEFUL_DOMAINS=[alias].tunnelto.dev APP_URL=http://dev.local:4200 SESSION_DOMAIN=.dev.local SANCTUM_STATEFUL_DOMAINS=dev.local:4200 APP_URL=http://localhost:4200 SESSION_DOMAIN=localhost SANCTUM_STATEFUL_DOMAINS=localhost:4200
- Angular 侧请求实现代码:
const httpOptions = { headers: new HttpHeaders({ 'Access-Control-Allow-Origin':'*', 'Access-Control-Allow-Credentials':'true', 'Accept': 'application/json', }), withCredentials: true, }; ... checkSession() { return this.http.post(apiUrl + '/session', {}, httpOptions); }
- 排查日志:逐步骤对比浏览器、服务端存储的会话 Cookie,二者值逐次匹配,但每次请求后 Cookie 值都会刷新:
Browser Cookie between CSRF and Auth: eyJpdiI6Ilk3M09RSU0zdVhae... Server Cookie from Auth: eyJpdiI6Ilk3M09RSU0zdVhae... Browser Cookie between Auth and Session Check: eyJpdiI6Ill1SWtOSlNXZytGQ... Server Cookie from Session Check: eyJpdiI6Ill1SWtOSlNXZytGQ... Browser Cookie after Session Check: eyJpdiI6InRrRlNodHVORXVtS...
修复方案
按以下顺序逐一排查调整,全部配置正确后问题即可解决:
1. 删除Angular请求头中错误的CORS字段
Access-Control-Allow-Origin、Access-Control-Allow-Credentials 是服务端返回的CORS响应头,前端主动在请求中携带这两个字段会触发CORS预检异常,直接从请求头中删除,修正后代码如下:
const httpOptions = { headers: new HttpHeaders({ 'Accept': 'application/json', }), withCredentials: true, };
2. 修正Laravel中间件加载顺序
打开 app/Http/Kernel.php,将 Sanctum 提供的 EnsureFrontendRequestsAreStateful 中间件放到 api 中间件组的最顶部,该中间件是SPA会话认证的核心,位置错误会导致会话无法被正确加载:
protected $middlewareGroups = [ 'api' => [ \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // 必须放在最前面 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
注意:不要给API路由套web中间件组,否则会触发CSRF校验报错。
3. 固定本地开发环境配置
本地走Angular代理的场景下,直接使用以下配置即可,不需要测试多域名、前缀点等无效配置:
APP_URL=http://localhost:4200 SESSION_DOMAIN=localhost SANCTUM_STATEFUL_DOMAINS=localhost:4200 # 本地HTTP访问必须设为false,HTTPS环境调整为true SESSION_SECURE_COOKIE=false SESSION_SAME_SITE=lax # 会话驱动默认用file即可,不要随意切换为其他驱动 SESSION_DRIVER=file
配置完成后执行 php artisan config:clear 清除配置缓存。
4. 补全Angular代理的Cookie重写规则
代理配置缺少Cookie重写规则会导致后端返回的会话Cookie无法正确写入浏览器当前域,打开Angular的 proxy.conf.json,给两个代理规则补充配置:
{ "/sanctum": { "target": "http://127.0.0.1:8000", // 替换为实际的Laravel服务地址 "secure": false, "changeOrigin": true, "cookieDomainRewrite": "localhost" }, "/api": { "target": "http://127.0.0.1:8000", "secure": false, "changeOrigin": true, "cookieDomainRewrite": "localhost" } }
5. 修复Laravel会话目录权限
日志中每次请求后Cookie值都会刷新,本质是Laravel无法写入会话文件,每次请求都生成新会话,自然无法保留登录状态。给项目storage目录递归赋可写权限:
chmod -R 755 storage # Windows/WSL环境将用户组替换为当前运行PHP的用户即可 chown -R www-data:www-data storage
6. 严格遵循请求顺序
认证相关请求必须按以下顺序发起,不能跳步:
- 第一步:请求
GET /sanctum/csrf-cookie,等待浏览器写入XSRF-TOKEN Cookie - 第二步:发起登录请求,Angular会自动从Cookie中读取XSRF-TOKEN携带到请求头
- 第三步:登录成功后再请求受保护接口
内容的提问来源于stack exchange,提问作者Tim Ferrill
相关产品推荐
相关产品推荐

