适用于单页面应用的轻量级用户认证解决方案 Laravel Airlock 功能速览
声明:因商标名称纠纷,Airlock 已经更名为 Sanctum。
昨天我在知识星球上提到了 Laravel Airlock 这个即将发布的官方扩展包,那个时间点 Taylor Otwell(Laravel 框架作者)也是刚刚将扩展包源码提 Github,以便让开发者参与早期测试和体验:
今天,学院君就带大家来体验下这个扩展包的使用。
为什么提供这个扩展包
Laravel Airlock 的官方定位是适用于单页面应用(SPA)和简单 API 接口的轻量级用户认证解决方案,所以它的定位仍然是 API 认证解决方案。
有人说不是已经有 Laravel Passport 这个 API 认证解决方案了吗?但是用过的同学应该都知道 Passport 这套东西还是比较复杂,对于单页面应用或者简单 API 接口有点杀机焉用牛刀的感觉(学院君曾经花过多达七篇教程来系统介绍 Passport 的功能)。
又有人会说,Laravel 也提供了基于 token
驱动的 API 认证了啊,Oh My God,那就是个玩具而已,只能在本地开发或者演示项目中用用,生产环境还是算了,不够安全。
基于此背景,Airlock 应运而生。
Laravel Airlock 使用入门
接下来,学院君给大家简单介绍下如何在 Laravel 项目中集成并使用 Airlock 进行 API 认证。
你也可以在 GitHub 上查看项目源码和英文文档:https://github.com/laravel/airlock。
安装配置
Laravel Airlock 仅支持 Laravel 6 及以上版本,所以我们需要在一个基于 Laravel 6 驱动的应用中通过 Composer 安装这个扩展包:
composer require laravel/airlock
安装完成后,通过 vendor:publish
命令发布扩展包的配置和迁移文件:
php artisan vendor:publish --provider="Laravel\Airlock\AirlockServiceProvider"
最后,运行数据库迁移命令生成扩展包需要的数据表:
php artisan migrate
该命令会新创建一个 personal_access_tokens
表用于存放用户 API 认证信息:
接下来,如果你是为单页面应用提供认证的话,可以在配置文件 config/airlock.php
中通过 stateful
配置请求来源域名,以便系统确认哪些域名需要维护认证状态(通过在这些域名下设置认证相关 Cookie 实现,通常,你可以将系统所有域名配置进来):
'stateful' => [
'laravel6.test',
'h5.laravel6.test',
],
另外在 airlock.php
配置文件中还有一个 expiration
配置项,用于配置令牌过期时间(单位为分钟),默认为 null
表示永远不过期。
设置中间件和认证守卫
接下来,我们要在 app/Http/Kernel.php
的 api
中间件分组中应用 Airlock 中间件:
use Laravel\Airlock\Http\Middleware\EnsureFrontendRequestsAreStateful;
...
protected $middlewareGroups = [
...
'api' => [
EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
'bindings',
],
];
...
该中间件会判断请求是否来自前面 stateful
配置项中的前端域名,如果是的话会进行 CSRF 验证确保请求安全、启动 Session、设置 Cookie、并在请求中标识 airlock
:
接下来,我们就可以使用 auth:airlock
中间件指定认证守卫为 Airlock 了,在 routes/api.php
中,我们可以这样定义基于 Airlock 认证的 API 路由:
Route::middleware('auth:airlock')->get('/user', function (Request $request) {
return $request->user();
});
如果这个 API 路由还对第三方应用提供了基于 Passport 的 OAuth2 认证,你可以通过指定多个认证守卫让它们同时生效:
Route::middleware('auth:airlock,passport')->get('/user', function (Request $request) {
return $request->user();
});
Airlock 认证底层实现原理
具体的 Airlock 认证守卫实现逻辑定义在 vendor/laravel/airlock/src/Guard.php
中,核心逻辑位于 __invoke
魔术方法:
如果用户已经通过 auth:web
认证,则判断用户模型是否支持 API 令牌(判断依据是是否使用了 HasApiTokens
Trait),如果支持则在用户实例上设置令牌并返回,否则直接返回用户实例。
如果用户没有通过 auth:web
认证,则在用户模型支持 API 令牌,并且在 Bearer Authorization 请求头中存在令牌信息的情况下,根据该令牌信息获取用户令牌模型实例(对应 personal_access_tokens
表记录),如果令牌不存在或者已过期则认证失败;否则返回令牌模型关联的包含令牌字段的用户实例。
其他情况下,认证失败。
基于以上源码分析,要让 Airlock 认证生效,还要在用户模型中使用 HasApiTokens
Trait:
use Laravel\Airlock\HasApiTokens;
class User extends Authenticatable
{
use Notifiable,HasApiTokens;
...
基于 Airlock 进行 API 认证
要认证单页面应用,应用的登录页需要先请求 /airlock/csrf-cookie
路由来初始化 CSRF 保护,我们编写一段简单的客户端测试代码示例如下:
window.axios.get('/airlock/csrf-cookie').then(response => {
window.axios.post('/airlock/token', {
'email': 'test@xueyuanjun.com',
'password': 'test123456'
}).then(response => {
console.log('认证成功');
let authToken = response.data;
window.axios.defaults.headers.common['Authorization'] = `Bearer ${authToken}`;
window.axios.get('/api/user').then(response => {
// 打印用户信息
console.log(response.data)
});
}).catch(error => {
// 登录失败,打印错误信息
console.log(error);
});
});
CSRF 保护初始化之后,可以发送 POST 请求到 /airlock/token
路由通过用户登录凭证(邮箱/密码)获取认证令牌,然后后续 API 接口请求就可以带上这个认证令牌通过 Airlock 守卫实现自动认证。
/airlock/csrf-cookie
路由已经由 Airlock 扩展包提供了,这里我们还需要在 routes/web.php
中新增 /airlock/token
路由定义:
use App\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
Route::post('/airlock/token', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => '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('auth-token')->plainTextToken;
});
这里的主要逻辑是验证用户注册邮箱和密码是否匹配,如果匹配的话则通过 HasApiTokens
提供的 createToken
方法颁发令牌。
接下来,我们编译前端资源,并访问引用上述 Axios 代码的页面,F12 打开控制台,可以在网络请求中看到上述请求都已经成功:
在 Console 中可以看到打印的日志信息,表明 API 认证接口请求成功:
小结
本篇教程只是抛砖引玉,Airlock 还支持撤销令牌、自定义用户模型及令牌模型,更多关于 Airlock 的使用方法请关注 laravel/airlock 这个项目。
最后简单总结下,Airlock 是一个轻量级的 API 认证解决方案,尤其适用于单页面应用,与 Passport 相比,更加精简,没有那么臃肿,与之前的 Token 方案相比,虽然也是基于 API 令牌,但更加安全,支持 CSRF 保护,支持令牌作用域和权限,支持令牌撤销。
声明:Laravel Airlock 目前还处于开发测试阶段,不建议在生产环境使用。
4 Comments
引用:又有人会说,Laravel 也提供了基于 token 驱动的 API 认证了啊,Oh My God,那就是个玩具而已,只能在本地开发或者演示项目中用用,生产环境还是算了,不够安全。
文章不错,不过上面那句话我没有看懂,希望看到是技术角度解释一下上面句话,谢谢。 请教laravel自带的API认证哪方面不安全?可以说出哪一点或者几点吗?
Laravel 官方文档自己也说了 不建议在生产环境使用这个默认的 token 驱动做 API 认证 在我看来 原因如下:
token 一旦生成就固定下来,没有有效期管理机制,并且适用于所有作用域,这意味着,一旦被黑客抓包截取,就可以使用它来访问应用的所有认证 API,并且拥有超级权限,永远有效,所以存在很大的安全隐患。
多谢提醒,不过api token 自己做一个过期机制加上https 也是可用的。
但是只能有一个令牌,而且没有令牌作用域。 还有一点是这个令牌是跟用户绑定的,也就是说必须是用户的令牌,而sanctum可以给任何数据颁发令牌(因为表设计是多态一对多的),且sanctum提供了更多的认证方式,比如单页面应用令牌,在单页面场景下使用后可以用cookie+session的方式。这些都是原token做不到的。