跳转到主要内容

什么是 Sanctum

Laravel Sanctum 是面向 SPA(单页应用)、移动应用以及简单 API 的轻量认证包。无需复杂的 OAuth 知识,也能为每个用户发放并管理多个 API 令牌。 Sanctum 解决两大问题:
认证模式机制主要用途
API 令牌认证Authorization: Bearer <token>移动应用、第三方集成
SPA 认证Session Cookie + CSRF 保护自家前端(Vue / React 等)
从自家 SPA 调用 API 时使用 SPA 认证。移动应用或第三方调用 API 时使用 API 令牌认证。也可以只使用其中一种。

与 Passport 的选择

SanctumPassport
复杂度简单完整 OAuth2
适合用途自家 SPA、移动应用面向外部应用的 OAuth 提供方
令牌类型个人访问令牌OAuth2 访问令牌
如果需要作为 OAuth2 提供方对接外部服务,请选择 Passport;大多数应用使用 Sanctum 就够了。

安装与配置

安装

只需执行 install:api Artisan 命令即可完成 Sanctum 的配置。
php artisan install:api
该命令会自动完成:
  • 安装 laravel/sanctum
  • 发布 personal_access_tokens 表的迁移文件
  • 执行迁移

添加 HasApiTokens trait

User 模型上添加 HasApiTokens trait。
// app/Models/User.php

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}
这样就可以使用 $user->createToken()$user->tokens 等方法。

API 令牌认证

令牌流程

签发令牌

通过 createToken() 签发令牌。可以通过 plainTextToken 属性获取明文令牌值。明文令牌不会保存到数据库,因此签发后必须立即返回给用户。
use Illuminate\Http\Request;

Route::post('/tokens/create', function (Request $request) {
    $token = $request->user()->createToken($request->token_name);

    return ['token' => $token->plainTextToken];
})->middleware('auth');
数据库中保存的是经 SHA-256 哈希后的令牌。

设置 Scope(能力)

可以为令牌附加能力(scope),限定该令牌能够执行的操作。
// 签发带 scope 的令牌
$token = $user->createToken('mobile-app', ['server:update', 'server:read']);

return $token->plainTextToken;
处理请求时检查令牌的 scope:
if ($request->user()->tokenCan('server:update')) {
    // 执行更新操作
}

if ($request->user()->tokenCant('server:update')) {
    abort(403);
}

通过中间件校验 scope

bootstrap/app.php 注册中间件别名:
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'abilities' => CheckAbilities::class,  // 拥有所有能力
        'ability'   => CheckForAnyAbility::class, // 拥有任一能力
    ]);
})
在路由上应用中间件:
// 只允许同时具备 check-status 和 place-orders 的令牌
Route::get('/orders', function () {
    // ...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);

// 允许具备 check-status 或 place-orders 之一的令牌
Route::get('/orders', function () {
    // ...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);

令牌有效期

默认情况下 Sanctum 令牌没有有效期。可通过 config/sanctum.phpexpiration 选项以分钟为单位设置。
// config/sanctum.php
'expiration' => 525600, // 365 天(分钟)
也可以按令牌指定有效期:
$token = $user->createToken(
    'token-name',
    ['*'],
    now()->addWeeks(1) // 1 周后过期
)->plainTextToken;
若设置了有效期,请定期清理过期令牌。
use Illuminate\Support\Facades\Schedule;

Schedule::command('sanctum:prune-expired --hours=24')->daily();

吊销令牌

// 删除全部令牌
$user->tokens()->delete();

// 删除当前请求所使用的令牌
$request->user()->currentAccessToken()->delete();

// 删除特定令牌
$user->tokens()->where('id', $tokenId)->delete();

SPA 认证

SPA 认证使用 Session Cookie,无需签发和管理令牌。适合自家前端(Vue、React、Next.js 等)调用 API 的场景。
使用 SPA 认证要求 SPA 与 API 共享同一顶级域名(子域名可以不同)。同时请求需包含 Accept: application/json 头和 RefererOrigin 头。

启用 Sanctum 中间件

bootstrap/app.php 中启用 statefulApi()
->withMiddleware(function (Middleware $middleware): void {
    $middleware->statefulApi();
})

配置一方(First-party)域名

config/sanctum.phpstateful 选项中配置 SPA 的域名。
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
    '%s%s',
    'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
    Sanctum::currentApplicationUrlWithPort()
))),

CORS 配置

若从其他子域名调用 API,需要配置 CORS。
php artisan config:publish cors
config/cors.php 中将 supports_credentials 设置为 true
// config/cors.php
'supports_credentials' => true,
前端 axios 也需配置:
// resources/js/bootstrap.js
axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;
别忘了设置 Session Cookie 域名:
// config/session.php
'domain' => '.example.com', // 前面加点

来自 SPA 的认证流程

1

获取 CSRF Cookie

登录前访问 /sanctum/csrf-cookie 初始化 CSRF 保护。
await axios.get('/sanctum/csrf-cookie');
2

发送登录请求

/login 发送 POST 请求。
await axios.post('/login', {
    email: '[email protected]',
    password: 'password',
});
3

发送已认证请求

登录后的请求会通过 Session Cookie 自动认证。
const response = await axios.get('/api/user');
console.log(response.data); // 已认证用户信息

保护已认证路由

在路由上应用 auth:sanctum 中间件,未认证的请求会返回 401 Unauthorized。API 令牌认证与 SPA 认证都由同一个中间件处理。
use Illuminate\Http\Request;

// 保护单一路由
Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

// 保护路由组
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/profile', [ProfileController::class, 'show']);
    Route::put('/profile', [ProfileController::class, 'update']);
    Route::get('/posts', [PostController::class, 'index']);
});

实用示例:登录 API 与返回令牌

下面是移动应用使用 API 令牌认证的示例。
1

创建登录端点

// routes/api.php

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

Route::post('/sanctum/token', function (Request $request) {
    $request->validate([
        'email'       => ['required', 'email'],
        'password'    => ['required'],
        'device_name' => ['required', 'string'],
    ]);

    $user = User::where('email', $request->email)->first();

    if (! $user || ! Hash::check($request->password, $user->password)) {
        throw ValidationException::withMessages([
            'email' => ['提供的凭据不正确。'],
        ]);
    }

    return response()->json([
        'token' => $user->createToken($request->device_name)->plainTextToken,
    ]);
});
2

创建已认证路由

// routes/api.php

Route::middleware('auth:sanctum')->group(function () {
    // 返回当前用户信息
    Route::get('/user', function (Request $request) {
        return $request->user();
    });

    // 吊销当前令牌并登出
    Route::post('/logout', function (Request $request) {
        $request->user()->currentAccessToken()->delete();

        return response()->json(['message' => '已登出。']);
    });

    // 登出所有设备
    Route::post('/logout/all', function (Request $request) {
        $request->user()->tokens()->delete();

        return response()->json(['message' => '已从所有设备登出。']);
    });
});
3

从客户端发起请求

// 登录
const { data } = await axios.post('/api/sanctum/token', {
    email: '[email protected]',
    password: 'password',
    device_name: 'My iPhone',
});

const token = data.token;

// 已认证请求
const response = await axios.get('/api/user', {
    headers: {
        Authorization: `Bearer ${token}`,
    },
});

测试

在 Sanctum 测试中,使用 Sanctum::actingAs() 认证用户并指定要授予的能力。
use App\Models\User;
use Laravel\Sanctum\Sanctum;

test('可以获取任务列表', function () {
    Sanctum::actingAs(
        User::factory()->create(),
        ['view-tasks']
    );

    $response = $this->get('/api/tasks');

    $response->assertOk();
});

test('拥有全部能力的令牌可访问', function () {
    Sanctum::actingAs(
        User::factory()->create(),
        ['*']
    );

    $response = $this->get('/api/tasks');

    $response->assertOk();
});

小结

# 安装 Sanctum 并执行迁移
php artisan install:api
在 User 模型上添加 HasApiTokens trait:
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}
// 签发令牌
$token = $user->createToken('token-name')->plainTextToken;

// 签发带 scope 的令牌
$token = $user->createToken('token-name', ['read', 'write'])->plainTextToken;

// 检查能力
$user->tokenCan('read');   // true/false
$user->tokenCant('write'); // true/false

// 吊销令牌
$user->tokens()->delete();                        // 全部令牌
$request->user()->currentAccessToken()->delete(); // 当前令牌
$user->tokens()->where('id', $id)->delete();      // 特定令牌
  • API 令牌认证:适用于移动应用、第三方、CLI 工具等无 Session 的客户端。
  • SPA 认证:适用于自家 Vue / React / Next.js 前端等同一域名(或子域名)上的 SPA。更安全,无需管理令牌。
最后修改于 2026年7月13日