Sanctum 不支持应用级 API 令牌,因其专为用户会话设计,强制绑定 tokenable_type/tokenable_id、短期可撤销;需服务间静态密钥时应使用自定义 api_token 字段+中间件校验。

Sanctum 本身不提供“应用级 API 令牌”(即全局、长期有效的 token),它专为 SPA、移动端或第一方客户端设计,依赖 tokenable_type + tokenable_id 绑定用户实例,且默认 token 是短期、可撤销、带作用域的。如果你需要类似 APP_API_TOKEN=xxx 这种服务间调用用的静态密钥,别硬套 Sanctum——它不是为此设计的。
为什么不能直接用 Sanctum::createToken() 生成应用令牌
Sanctum 的 createToken() 方法必须传入一个实现了 CanUseSanctumTokens 的模型实例(通常是 User),返回的是与该用户强绑定的 Personal Access Token。它:
- 强制要求数据库写入
personal_access_tokens表 - 每次调用都生成新记录,无法复用或预设
- 无法绕过用户上下文(没有“无用户”的 token)
- 不支持服务端到服务端(machine-to-machine)场景下的静态凭证校验
替代方案:用 Laravel 自带的 API Token 字段 + 中间件校验
适合内部服务通信、CLI 调用、Webhook 验证等无需用户会话的场景。核心是:自己管 token 存储、自己写校验逻辑。
实操步骤:
- 在
users表中添加字段:api_token(string,唯一索引,可为空) - 运行迁移:
php artisan make:migration add_api_token_to_users_table
public function up(MigrationBuilder $migration) { $migration->table('users', function (Blueprint $table) { $table->string('api_token')->unique()->nullable(); $table->index('api_token'); }); } - 生成并保存 token(例如在 tinker 或用户编辑页):
use Illuminate\Support\Str; $user = App\Models\User::find(1); $user->api_token = Str::random(60); $user->save();
- 编写中间件
EnsureApiToken:php artisan make:middleware EnsureApiToken
public function handle($request, Closure $next) { $token = $request->bearerToken() ?? $request->query('api_token'); if (!$token || !User::where('api_token', $token)->exists()) { return response(['message' => 'Unauthorized'], 401); } return $next($request); } - 注册中间件并使用:
Route::middleware('ensure.api.token')->group(function () { Route::get('/internal/data', [DataController::class, 'index']); });
如果坚持要用 Sanctum 模拟“应用令牌”,必须绕过其默认行为
可行但不推荐:手动插入一条伪造的 personal_access_tokens 记录,把 tokenable_id 设为 0、tokenable_type 设为空字符串或自定义值(如 'app'),再写一个中间件跳过用户绑定校验。
风险点:
- 破坏 Sanctum 的安全假设(如 token 撤销、作用域控制失效)
- 升级 Sanctum 后可能因 schema 或校验逻辑变更导致崩溃
- 无法用
Sanctum::currentAccessToken()获取上下文 - 日志和审计缺失用户来源信息
若真要这么做,至少加一层防护:
DB::table('personal_access_tokens')->insert([
'tokenable_type' => 'App\Models\Application',
'tokenable_id' => 1,
'name' => 'internal-api-key',
'token' => hash_hmac('sha256', Str::random(40), config('app.key')),
'abilities' => ['*'],
'created_at' => now(),
'updated_at' => now(),
]);然后自定义中间件查 tokenable_type = 'App\Models\Application',而不是靠 Sanctum::authenticate()。
真正需要应用级令牌时,优先考虑独立的认证层(如 Laravel Passport 的 client credentials flow)、环境变量 + 简单中间件,或者外部密钥管理服务。Sanctum 的定位很清晰:用户会话代理,不是通用 token 发行中心。










