引言
随着现代 Web 应用日益采用单页应用(SPA)架构,实现安全的身份验证变得至关重要。Laravel Sanctum 为 SPA 身份验证提供了优雅的解决方案,相比传统基于令牌的方案具有更高的安全性。本指南将带您创建 Laravel API 应用,并实现基于 Cookie 的 Sanctum 身份验证。
什么是基于 Cookie 的身份验证?
基于 Cookie 的身份验证利用 HTTP-only Cookie 安全存储会话信息。与 localStorage 或 sessionStorage 存储的令牌不同,HTTP-only Cookie 无法被 JavaScript 访问,天然具备防御 XSS 攻击的能力。
Laravel Sanctum 的基于 Cookie 的身份验证专为前端与后端共享相同顶级域名的 SPA 设计,无需将敏感令牌暴露给客户端 JavaScript 即可实现无缝身份验证。
为何选择基于 Cookie 而非令牌?
- XSS 防护:HTTP-only Cookie 无法被 JavaScript 访问,防止 XSS 攻击窃取令牌
- 自动管理:浏览器自动处理 Cookie 存储和传输
- 会话安全:Laravel 内置会话管理提供额外安全层
开发者体验优势
- 无缝集成:与 Laravel 现有身份验证系统自然协作
- 免令牌管理:无需手动处理令牌存储、刷新或过期
- 内置 CSRF 保护:利用 Laravel 的 CSRF 防护机制
创建 Laravel 项目
创建专为 API 开发优化的 Laravel 应用:
laravel new sanctum-api
cd sanctum-api
选择 “No Starter Kit”(无入门套件)以手动配置完整流程。
安装配置 Sanctum
步骤 1:安装 Sanctum API
php artisan install:api
此命令自动完成:
- 安装 Laravel Sanctum 包
- 创建带中间件的
routes/api.php
文件 - 在
bootstrap/app.php
配置 Sanctum 中间件 - 注册 API 路由
步骤 2:配置有状态 API
在 bootstrap/app.php
中配置:
->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi(); // 启用基于Cookie的身份验证
})
步骤 3:配置 Sanctum 设置
编辑 config/sanctum.php
:
'prefix' => 'api', // 统一API前缀
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
'validate_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
],
步骤 4:配置前端域名
在 .env
文件中指定:
# 开发环境
SANCTUM_STATEFUL_DOMAINS=localhost:3000,127.0.0.1:3000
# 生产环境
SANCTUM_STATEFUL_DOMAINS=myapp.com,api.myapp.com
关键配置说明:
- 不包含
http://
或https://
- 不添加开头/结尾斜杠
- 包含非标准端口
- 前端与后端必须共享相同顶级域名
创建身份验证系统
步骤 1:创建控制器
php artisan make:controller AuthController
步骤 2:实现登录/退出逻辑
class AuthController extends Controller
{
public function login(Request $request): JsonResponse
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required', 'string'],
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate(); // 重新生成会话ID
return response()->json([
'message' => '身份验证成功',
'user' => Auth::user(),
]);
}
throw ValidationException::withMessages([
'email' => ['提供的凭据不正确'],
]);
}
public function logout(Request $request): JsonResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return response()->json(['message' => '已成功退出']);
}
public function user(): JsonResponse
{
return response()->json(['user' => Auth::user()]);
}
}
步骤 3:注册路由
在 routes/api.php
中添加:
Route::prefix('auth')->group(function () {
Route::post('login', [AuthController::class, 'login']);
Route::post('logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');
});
Route::middleware('auth:sanctum')->get('user', [AuthController::class, 'user']);
设置测试数据
php artisan db:seed
默认创建用户:
- 邮箱:
test@example.com
- 密码:
password
自定义测试数据可修改 DatabaseSeeder.php
。
身份验证流程
认证流程步骤:
- 获取 CSRF Cookie:从 Laravel 获取 CSRF 令牌
- 登录请求:使用凭据和 CSRF 令牌认证
- 认证请求:使用会话 Cookie 进行后续请求
示例请求:
### 1. 获取CSRF Cookie
GET /api/sanctum/csrf-cookie
Origin: localhost
### 2. 登录(使用提取的XSRF-TOKEN)
POST /api/auth/login
X-XSRF-TOKEN: [令牌值]
Content-Type: application/json
{
"email": "test@example.com",
"password": "password"
}
### 3. 访问受保护路由
GET /api/user
X-XSRF-TOKEN: [令牌值]
常见错误解决方案
错误 1:“Session store not set on request” (500错误)
原因:statefulApi()
需要 Origin
或 Referer
标头验证请求来源
解决:所有请求添加 Origin
标头:
Origin: localhost
错误 2:“CSRF token mismatch” (419错误)
解决步骤:
- 先请求
/api/sanctum/csrf-cookie
- 正确提取 Cookie 值(移除 URL 编码)
- 所有修改状态请求添加
X-XSRF-TOKEN
标头 - 检查 Cookie 是否过期
错误 3:“Unauthenticated” (401错误)
排查方向:
- 请求缺失会话 Cookie
- 会话过期或无效
- 前端域名未在
SANCTUM_STATEFUL_DOMAINS
中配置
错误 4:“Preflight wildcard origin not allowed”
解决:在 config/cors.php
配置具体域名:
'allowed_origins' => ['http://localhost:3000'],
'supports_credentials' => true, // 关键配置
前端集成示例
Axios 配置
const api = axios.create({
baseURL: 'http://localhost:8000/api',
withCredentials: true, // 启用Cookie传输
});
// 自动添加CSRF令牌
api.interceptors.request.use(config => {
const token = document.cookie.match('XSRF-TOKEN=([^;]+)')?.[1];
if (token) config.headers['X-XSRF-TOKEN'] = token;
return config;
});
Vue.js 身份验证服务
class AuthService {
async login(credentials) {
await api.get('/sanctum/csrf-cookie'); // 先获取CSRF
return api.post('/auth/login', credentials);
}
async logout() {
return api.post('/auth/logout');
}
}
React 认证钩子
function useAuth() {
const [user, setUser] = useState(null);
useEffect(() => { checkAuth() }, []);
const checkAuth = async () => {
try {
const { data } = await api.get('/user');
setUser(data.user);
} catch {
setUser(null);
}
};
// 登录/退出方法
return { user, login, logout };
}
安全最佳实践
会话管理加固
public function login(Request $request) {
if (Auth::attempt($credentials)) {
$request->session()->regenerate(); // 认证后重新生成会话ID
// ...
}
}
生产环境配置
# .env
APP_ENV=production
APP_DEBUG=false
SESSION_SECURE_COOKIE=true # 仅HTTPS传输Cookie
SESSION_SAME_SITE=lax
速率限制
// 限制认证接口请求频率
Route::middleware('throttle:auth')->post('/auth/login', ...);
强制 HTTPS
// AppServiceProvider.php
public function boot() {
if (app()->environment('production')) {
URL::forceScheme('https');
}
}
生产部署清单
-
环境配置:
- 设置
APP_ENV=production
- 禁用
APP_DEBUG
- 配置生产环境域名
- 设置
-
安全加固:
- 全站启用 HTTPS
- 配置认证接口速率限制
- 设置严格的 CORS 策略
-
性能优化:
- 使用 Redis 缓存会话
- 配置会话垃圾回收
- 监控认证失败率
故障排除流程
-
基础配置检查:
- Sanctum 是否正确安装
statefulApi()
中间件是否启用- 前端域名是否在白名单
-
请求头验证:
- 包含
Accept: application/json
Origin
标头与配置匹配X-XSRF-TOKEN
存在于修改状态请求
- 包含
-
CSRF 流程:
- 认证前是否获取了 CSRF Cookie
- 是否正确提取和传递令牌
-
CORS 配置:
supports_credentials
设为true
- 使用具体域名(禁用通配符 *)
-
会话管理:
- 会话驱动配置是否正确
- Cookie 是否正确设置和传输
结语
Laravel Sanctum 的基于 Cookie 身份验证为 SPA 提供了强大而安全的解决方案。通过利用 HTTP-only Cookie 和 Laravel 内置会话管理,开发者可以实现既安全又高效的身份验证系统。
成功实施的关键在于深入理解完整流程:获取 CSRF 令牌、正确配置 CORS 以及确保请求头的一致性。结合适当的安全措施和测试,Sanctum 将成为现代 Web 应用不可或缺的工具。
参考资源: