Sanctum 认证
Laravel Sanctum
介绍
Laravel Sanctum 为 SPA (单页面应用程序)、移动应用程序和简单的、基于令牌的 API 提供轻量级身份验证系统。Sanctum 允许应用程序的每个用户为他们的帐户生成多个 API 令牌。这些令牌可能被授予指定允许令牌执行哪些操作的能力/范围。
工作原理
Laravel Sanctum 是为了解决两个独立问题而生。
API 令牌
首先,它是一个简单的包,用于向用户发出 API 令牌,而不涉及 OAuth。这个功能的灵感来自 GitHub 的「访问令牌」。例如,假设应用程序的「帐户设置」有一个界面,用户可以在其中为其帐户生成 API 令牌。您可以使用 Sanctum 来生成和管理这些令牌。这些令牌通常有很长的过期时间(以年计),当然用户是可以随时手动撤销它们的。
Laravel Sanctum 的这个特性是通过将用户 API 令牌存储在单个数据库表中,并通过包含了有效 API 令牌的 Authorization
标头对传入请求进行身份验证而实现的。
SPA 身份验证
Sanctum 提供了一种简单的方法来认证需要与基于 Laravel 的 API 进行通信的单页应用程序 (SPAs)。这些 SPA 可能与 Laravel 应用程序存在于同一仓库中,也可能是一个完全独立的仓库,例如使用 Vue CLI 创建的单页应用程序。
对于此功能,Sanctum 不使用任何类型的令牌。相反,Sanctum 使用 Laravel 内置的基于 cookie 的会话身份验证服务。这提供了CSRF保护,会话身份验证以及防止因 XSS 攻击而泄漏身份验证凭据。仅当传入请求来自您自己的 SPA 前端时,Sanctum 才会尝试使用 Cookie 进行身份验证。
技巧:Sanctum 适用于 API 令牌认证或 SPA 身份认证,使用 Sanctum 并不意味着你需要用到它所提供全部特性。
安装
你可以通过 Composer 安装 Laravel Sanctum:
composer require laravel/sanctum
接下来,你需要使用 vendor:publish
Artisan 命令发布 Sanctum 的配置和迁移文件。Sanctum 的配置文件将会保存在 config
文件夹中:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
最后,你需要执行数据库迁移文件。Sanctum 将创建一个数据库表用于存储 API 令牌:
php artisan migrate
假如你需要使用 Sanctum 来验证 SPA,你需要在 app/Http/Kernel.php
文件中将 Sanctum 的中间件添加到你的 api
中间件组中:
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
'api' => [
EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
自定义迁移
如果你不想使用Sanctum的默认迁移, 你可以在你的 AppServiceProvider
中的 register
里调用 Sanctum::ignoreMigrations
方法。 你可以使用 php artisan vendor:publish --tag=sanctum-migrations
导出默认迁移。
API令牌认证
技巧:当需要为 SPA 应用选择认证方案的时候,应该首选 Sanctum 内置的 SPA 认证 而不是 API 令牌。
发行API令牌
可以使用 Sanctum 发行 API 令牌 / 个人访问令牌 对你的 API 请求进行认证。 当使用 API 令牌进行请求的时,令牌可以以 Bearer
的形式包含在 Authorization header
头里。
给用户发行令牌的时候,User 模型里应该使用HasApiTokens
trait:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
要发行一个令牌,需要使用 createToken
方法。 createToken
方法返回一个 Laravel\Sanctum\NewAccessToken
实例。在存入数据库之前,API 令牌已使用 SHA-256 哈希加密过,但是可以用 NewAccessToken
实例的 plainTextToken
属性访问令牌的纯文本值。令牌创建后,应该立即向用户展示这个纯文本值:
$token = $user->createToken('token-name');
return $token->plainTextToken;
可以使用 HasApiTokens
trait 提供的 tokens
Eloquent 关联关系来获取所有的用户令牌:
foreach ($user->tokens as $token) {
//
}
令牌能力
Sanctum 可以为令牌分配“abilities”,类似于 OAuth 的“scopes”。可以将字符串能力数组作为第二个参数传递给 createToken
方法:
return $user->createToken('token-name', ['server:update'])->plainTextToken;
在使用 Sanctum 处理一个请求的时候,可以使用 tokenCan
方法来决定令牌是否具有给定的能力:
if ($user->tokenCan('server:update')) {
//
}
技巧:为了方便,如果你的 SPA 应用使用了 Sanctum 内置的 SPA认证,当一个已经认证的请求进来的时候,
tokenCan
方法将总是返回true
。
保护路由
为了保护路由,所有进来的请求都必须进行认证,应该将 Sanctum
认证守卫附加到 routes/api.php
的 API 路由里。如果一个请求是来自第三方的请求,这个守卫会确保进来的请求既是一个你的 SPA 应用的有状态的已认证请求,也是一个包含了有效令牌头的已认证请求:
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
撤销令牌
我们可以使用 HasApiTokens
trait 提供的 tokens
关联关系从数据库删除它们,以达到撤销令牌的目的:
// 撤销所有令牌...
$user->tokens()->delete();
// 撤销用户最近的令牌...
$request->user()->currentAccessToken()->delete();
// 撤销一个特定的令牌...
$user->tokens()->where('id', $id)->delete();
SPA认证
Sanctum 为单页面应用 (SPAs) 与 Laravel 支持的 API 之间进行通信提供了一套简便的认证方法。这些 SPAs 可以与你的 Laravel 应用在同一个存储层中,也可以完全分离于存储层之外,比如通过 Vue CLI 构建的 SPA。
对于这个特性,Sanctum 不使用其他任何类型的令牌。相反,Sanctum 使用的是 Laravel 内置的基于 cookie 的 session 认证服务。这带来了诸多好处,比如 CSRF 保护,以及防止通过 XSS 泄漏身份验证凭据。Sanctum 只会在传入的请求来自于你自己的 SPA 前端时尝试使用 cookie 进行身份验证。
注意:为了完成认证,你的 SPA 与 API 必须共享同一个顶级域名。 但是,它们可以被放置在不同的次级域名上。
配置
配置你的第一方域
首先,你应该配置你的单页面应用将从哪个域发出请求。你可以使用 Sanctum
配置文件中的 stateful
配置选项配置这些域。此配置设置确定在向你的 API 发出请求时,哪些域将使用 Laravel 会话 cookie 来维持 “有状态的” 身份验证。
注意:如果你可以通过一个带有端口号的 URL (
127.0.0.1:8000
) 进入你的应用,你必须保证你的域名里包含这个端口号。
Sanctum 中间件
接下来,你应该将 Sanctum
的中间件添加到 app/Http/Kernel.php
文件中的 api 中间件组中。该中间件负责确保来自单页面应用的传入请求可以使用 Laravel 的会话 cookie 进行身份验证,同时仍允许来自第三方或移动应用程序的请求使用 API 令牌进行身份验证:
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
'api' => [
EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
CORS & Cookies
如果你无法通过在单独的子域上执行的 SPA 对应用程序进行身份验证,则可能配置了错误的 CORS (跨源资源共享) 或会话 Cookie 设置。
你应该确保应用程序的 CORS 配置返回的 Access-Control-Allow-Credentials
标头的值为 true
。你可以在 cors
配置文件中配置应用程序的 CORS 设置。
另外,你应该在全局 axios
实例上启用 withCredentials
选项。通常,这应该在你的 resources/js/bootstrap.js
文件中执行:
axios.defaults.withCredentials = true;
最后,你应确保应用程序的会话 cookie 域配置支持根域的任何子域。您可以在您的 session
配置文件中用前导.
作为域的前缀:
'domain' => '.domain.com',
验证
要对单页面应用进行身份验证,你的单页面应用的登录页面应首先向 /Sanctum/csrf-cookie
路由发出请求,以初始化应用程序的 CSRF 保护:
axios.get('/sanctum/csrf-cookie').then(response => {
// 登录...
});
在此请求期间,Laravel将设置一个包含当前CSRF令牌的XSRF-TOKEN
cookie。然后,该令牌应该在后续请求的X-XSRF-token
头中传递,这是像Axios和Angular HttpClient这样的库自动为您完成的。
初始化 CSRF 保护后, 你应该对典型的 Laravel/login
路由发出 POST
请求。此 /login
路由可以由 laravel/jetstream
authentication scaffolding软件包提供。
如果登录请求成功,则将对你进行身份验证,并且随后通过 Laravel 后端发布给你的客户端的会话 cookie,自动验证对 API 路由的后续请求。
技巧:你可以自由编写自己的
/login
端点;但是,你应确保使用标准的 session based authentication services that Laravel provides 对用户进行身份验证。
路由保护
为了保护路由,因此必须对所有传入的请求进行身份验证,你应该在 routes/api.php
文件中为你的 API 路由附加 Sanctum
授权看守器。如果请求来自你的单页面应用,此看守器会确保传入的请求被验证为有状态的已验证请求,如果请求来自第三方,它将使请求包含有效的 API 令牌头:
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
授权私有广播频道
如果你的单页面应用需要通过 private / presence broadcast channels 进行身份认证,你需要在你的 routes/api.php
文件中调用 Broadcast::routes
方法:
Broadcast::routes(['middleware' => ['auth:sanctum']]);
接下来,为了使 Pusher 的授权请求成功,你需要在初始化 Laravel Echo 时提供自定义的 Pusher authorizer
。这可以让你的应用程序将 Pusher 配置为 为了跨域请求正确配置的 axios
实例properly configured for cross-domain requests:
window.Echo = new Echo({
broadcaster: "pusher",
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
encrypted: true,
key: process.env.MIX_PUSHER_APP_KEY,
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post('/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response.data);
})
.catch(error => {
callback(true, error);
});
}
};
},
})
移动应用身份验证
你可以使用 Sanctum 令牌的对你移动应用程序的路由请求进行身份认证。验证移动应用程序请求的过程与验证第三方接口请求的过程类似,但是,他们在颁发 API 令牌的方式上存在细微的差异。
颁发API令牌
开始时,创建接受用户的电子邮件 / 用户名、密码和设备名称的路由,然后将这些凭据交换为新的 Sanctum 令牌。终端将返回纯文本 Sanctum 令牌,然后该令牌可以存储在移动设备上,并用于发出其他 API 请求:
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',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
return $user->createToken($request->device_name)->plainTextToken;
});
当移动设备使用令牌向你的应用程序发出 API 请求时,它应将令牌作为 Bearer
令牌传递到 Authorization
请求头中。
技巧:在为移动应用程序发行令牌时,您还可以自由指定 token abilities。
路由保护
如前所述,您需要保护路由,因此必须通过在路由上附加 Sanctum
身份验证看守器来对所有传入请求进行身份验证。一般来说,你会将此看守器附加到 routes/api.php
文件中定义的路由上:
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
撤销令牌
为了允许用户撤销发布给移动设备的 API 令牌,你可以在 Web 应用程序 UI 的 “帐户设置” 部分中按名称列出它们,并带有 “撤销” 按钮。当用户单击 “撤消” 按钮时,可以从数据库中删除令牌。请记住,您可以通过 HasApiTokens
特性提供的 tokens
关系访问用户的 API 令牌:
// 撤销所有令牌...
$user->tokens()->delete();
// 撤销一个特定的令牌...
$user->tokens()->where('id', $id)->delete();
测试
在测试期间,Sanctum::actingAs
方法可用于验证用户身份并指定授予其令牌的能力:
use App\Models\User;
use Laravel\Sanctum\Sanctum;
public function test_task_list_can_be_retrieved()
{
Sanctum::actingAs(
User::factory()->create(),
['view-tasks']
);
$response = $this->get('/api/task');
$response->assertOk();
}
如果要授予令牌所有功能,则应在 actingAs
方法提供的功能列表中加入 *
:
Sanctum::actingAs(
User::factory()->create(),
['*']
);
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: