Laravel 中,实现基于传统表单的登录和授权已经非常简单,但是如何满足 API 场景下的授权需求呢?在 API 场景里通常通过令牌来实现用户授权,而非维护请求之间的 Session 状态。我们可以使用 Passport 轻而易举的实现 API 授权认证,Passport 可以在几分钟内为应用程序提供完整的 OAuth2 服务端实现。Passport 是基于由 Alex Bilbie 维护的 League OAuth2 Server 建立的。
在开始之前,请通过 Composer 包管理器安装 Passport:
composer require laravel/passport
Laravel 5.5 需要指定适配版本:
composer require laravel/passport=~4.0
如果 paragonie/random_compat
出现依赖版本冲突,可以先安装低版本:
composer require paragonie/random_compat:2.*
然后再安装 passport 即可。
Passport 扩展包里已经自动注册了迁移文件加载,执行 migrate
会自动运行扩展包里的迁移文件,由此来创建存储客户端和令牌的数据表:
php artisan migrate
如果不打算使用 Passport 的默认迁移,可以在
AppServiceProvider
的register
方法中调用Passport::ignoreMigrations
方法。然后通过php artisan vendor:publish --tag=passport-migrations
导出默认迁移。
默认会生成 5 张数据表:
oauth_access_tokens
oauth_auth_codes
oauth_clients
oauth_personal_access_clients
oauth_refresh_tokens
接下来,运行 passport:keys
命令来创建安全访问令牌时所需的加密密钥:
php artisan passport:keys
执行成功后,会在 storage
目录中看到两个以 oauth
开头的密钥对。
php artisan passport:client --password --name='bbs-ios'
passport:client
命令可以创建一个客户端,由于我们使用的是密码模式,所以需要增加 --password
参数。同时还可以增加 --name
参数为客户端起个名字,这里我们起名为 bbs-ios
。
命令行中已经输出了创建了 client_id
和 client_secret
。
安装好了 Passport 我们来调试一下,Passport::routes()
为我们提供了基础路由,我们先注册一下路由。
app/Providers/AuthServiceProvider.php
.
.
.
use Carbon\Carbon;
use Laravel\Passport\Passport;
.
.
.
public function boot()
{
$this->registerPolicies();
// Passport 的路由
Passport::routes();
// access_token 过期时间
Passport::tokensExpireIn(Carbon::now()->addDays(15));
// refresh_token 过期时间
Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
}
我们注册了路由,同时通过 Passport 的 tokensExpireIn
和 refreshTokensExpireIn
定义了访问令牌的过期时间,否则令牌是永久有效的。这里我们定义 access_token
15 天内有效,refresh_token
30 天内有效。
密码模式我们通过 domain.com/oauth/token
这个路由来获取访问令牌。提交的参数如下:
grant_type - 密码模式固定为 password
;
client_id - 通过 passport:client
创建的客户端 id
;
client_secret - 通过 passport:client
创建的客户端 secret
;
username - 登录的用户名;
password - 用户密码;
scope - 作用域,可填写 *
或者为空。
提交正确的 client
信息以及任意已存在用户的用户名和密码,即可获取到访问令牌:
token_type - 令牌类型;
expires_in - 多久之后过期,单位:秒;
access_token - 访问令牌;
refresh_token - 刷新令牌。
刷新访问令牌和获取访问令牌的接口地址一样,只是参数不同:
grant_type - 刷新令牌固定为 refresh_token
;
client_id - 通过 passport:client
创建的客户端 id
;
client_secret - 通过 passport:client
创建的客户端 secret
;
refresh_secret - 刷新令牌;
scope - 作用域,可填写 *
或者为空。
刷新令牌不用提交用户的用户名和密码,而是直接使用 refresh_token
换取新的访问令牌。
为什么要刷新 Access Token 呢?
一是因为 Access Token 是由过期时间的,到了过期时间这个 Access Token 就会失效,需要刷新;
二是因为一个 Access Token 会关联一定的用户权限,如果用户授权更改了,这个 Access Token 需要被刷新以关联新的权限。
为什么要专门用一个 Refresh Token 去更新 Access Token 呢?如果没有 Refresh Token,也可以更新,但每次都要用户手动输入用户名与密码,比较麻烦。有了 Refresh Token,就无需用户进行登录操作。
Refresh Token 也有过期时间,但是相对较长。Refresh Token 对存储的要求通常会非常严格,以确保它不会被泄露;Access Token 和 Refresh Token 都可以被授权服务器列入黑名单。
有了访问令牌,我们就能通过令牌获取个人用户信息了,不过在这之前我们需要修改一些配置。
将 Laravel\Passport\HasApiTokens
这个 Trait 添加到用户模型中,来给模型提供一些辅助函数,用于检查已认证用户的令牌和使用范围:
app/User.php
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
修改 auth
配置,将 api guard 的 driver
由 token
修改为 passport
。
config/auth.php
.
.
.
'guards' => [
.
.
.
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
]
.
.
.