使用场景:
例如:
- 某个网站,某用户未注册,注册时提示可微信账号登录(github, google都有类似
- 当然也可以直接用作API的验证
API登录验证场景(passport个人授权码):
提示:现在基本都是前后端分离了,所以前端要调用后台的接口,就有个验证的过程,一般情况都是用户登录后,后台返回一个token,然后前端在每次请求后台接口的时候都通过请求头将该token传给后台,后台进行验证。这种情况不一定要用passport,用jwt或者直接在数据库中加一个字段也行。
步骤:
- 创建一个laravel项目
composer create-project --prefer-dist laravel/laravel blog "8.5.*"
- 安装passport
composer require laravel/passport
- 我的mysql是5.6版本,所以在执行数据迁移之前先修改一下代码,不然会报错。在
App\Providers\AppServiceProvider
use Illuminate\Support\Facades\Schema;
public function boot()
{
//
Schema::defaultStringLength(191);
}
- 执行数据迁移,自己提前在.env文件配置好数据库
php artisan migrate
- 修改User模型,Models/User.php,这个看你自己项目的用户表在哪里,该trait提供一些常用方法
use Laravel\Passport\HasApiTokens;
use HasApiTokens,HasFactory, Notifiable;
- 修改conffig/auth.php,修改API的认证方式,将api的driver改为passport
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
],
- 生成安全访问令牌的加密秘钥。另外,该命令也将创建用于生成访问令牌的 “个人访问” 客户端和 “密码授权” 客户端 ,命令执行完后,会在oauth_clients表中新增两条数据和在项目storage目录下生成两文件,oauth-private.key,oauth-public.key
php artisan passport:install
###命令执行完,oauth_clients表中的两条数据,一条name=Laravel Personal Access Client的数据是"个人访问"客户端需要用到的,也就是我们现在的API这种模式,还要一条是name=Laravel Password Grant Client的数据是“密码授权” 客户端用到。这个“密码授权”简单的就是说,需要用户的账户密码,还有该条数据的id,secret等参数去请求passport自带的路由(http://passport-server/oauth/token),认证成功就会返回token
- 创建两个控制器来测试,并增加路由,一个是Login,一个User,Login不需要验证token,User需要验证token
//api.php路由文件,一个是有中间件,一个没有,中间件就是验证token的
Route::post('login',[\App\Http\Controllers\LoginController::class,'login']);
Route::get('user',[\App\Http\Controllers\UserController::class,'index'])->middleware('auth:api');
- LoginController的Login方法,我没有写具体的验证,我只是写怎么生成token
//
function login(Request $request){
//验证合法性等等。。。。
//登录成功,生成token
$user = User::where("name",'yang')->first();
$token = $user->createToken('personal token');
return $token;
}
- UserController用来获取用户信息,必须是登录成功才能获取,所以需要传递token
//LoginController中的index方法,user方法中的api参数就是在config/auth.php中的guards中的值,
//因为api guard不是默认的,所以需要显式的指定要用guard
function index(Request $request){
return $request->user('api');
}
- 返回结果,请求一定要在header头中带上token,格式是Authorization:Bearer Token。。。Bearer后面有一个空格
第三方登录(passport的授权码模式):
提示:大致流程是,
客户端==我自己某个网站
认证服务器==微信服务器
我的网站想接入微信的第三方登录,这样用户就不用在重新注册一个账号了,可以直接用微信用户的信息,但是微信不能平白无故给我们网站这些信息呀,所以就有了这种模式
- 用户访问客户端,客户端导向至认证服务器
- 用户如果没有在认证服务器上登录,那跳转到登录页面,然用户先登录认证服务器
- 登录成功后跳转到授权页面,也就是用户选择是否授权的页面
- 用户给予授权后,认证服务器会跳转到事先指定的客户端的某个地址,地址上并会附上一个code,该code是一次性的,并且时效很短
- 客户端收到该code后,会向认证服务器申请令牌,该过程是由客户端后台服务器上完成,用户不可见
- 认证服务器效验了code后,会返回access_token和refresh_token
具体实现(上面的配置依然有效,也就是上面API模式的步骤,下面也要配置):
- 先生成auth的前端内容
composer require laravel/ui --dev
- 在执行
php artisan ui vue --auth
- 这个时候访问登录等页面发现没有样式,在执行
npm install
npm run dev
- 再生成一个客户端,也就是在oauth_clients表中再加一条数据,该数据和之前那2条作用是不一样的,该命令执行后,会提示你输入名称和回调地址,也就是用户点击了允许授权后,认证服务器会跳转回该地址,并带有code
php artisan passport:client
- 在App\Providers\AuthServiceProvider的boot方法中注册passport默认自带的路由
use Laravel\Passport\Passport;
Passport::routes();
//注册有哪些scope,相当于设置权限,不设置就表示全部,这些scope需要在定义路由的时候指定好
/*Passport::tokensCan([
'view-posts' => 'Views',
'view-user' => 'View user',
]);*/
Passport::tokensExpireIn(now()->addHour(2)); // access_token 过期时间
Passport::refreshTokensExpireIn(now()->addDays(3)); // refresh_token 过期时间
Passport::personalAccessTokensExpireIn(now()->addSeconds(20));//个人访问令牌过期时间
- 直接访问认证服务器的oauth/authorize路由,这是passport自带的,参数换成刚刚生成那条客户端数据中的参数,就是oauth_clients表的数据
http://www.passportapi.com/oauth/authorize?client_id=3&redirect_uri=http://localhost/callback&response_type=code&scope=*
- 用户授权后,将会跳转回http://localhost/callback?code=xxxxxx,并会带有code码,客户端获取到这个码以后就去请求access_token和refre_token
- 获取到code后,那code和其他参数换取access_token,请求oauth/token路由
- 拿到token后客户端就可以用这个access_token去请求认证服务器上的接口了,这个token还是需要放在header请求头中
- 还有很多passport自带的路由,可以去官方文档看看(到这里其实可以告一段落了,后面只是补充)
- 多提一嘴,授权码模式还能指定更加细化的权限,细致到每个路由,用scope来指定
- 默认作用域
//如果客户端没有请求任何特定的范围,你可以在 App\Providers\AuthServiceProvider 类的 boot 方法中使用 Passport::setDefaultScope 方法来定义默认的作用域。
use Laravel\Passport\Passport;
//这里相当于指定所有的范围,place-order是scope的名,接口中scope参数需要的就是这个,Place orders只是描述,方便看而已
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
//这里是如果客户端没有指定范围,那我们可以给个默认的范围,*代表全部
Passport::setDefaultScope([
'check-status',
'place-orders',
]);
- 带有作用域的请求:place-orders
http://www.passportapi.com/oauth/authorize?client_id=3&redirect_uri=http://localhost/callback&response_type=code&scope=place-orders
-
个人访问令牌也可以指定作用域
//LoginController
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
- 有了作用域,应该怎么验证呢?先在认证服务器中加中间件
//Passport 包含两个中间件,可用于验证传入的请求是否包含访问指定作用域的令牌。 使用之前,需要将下面的中间件添加到 app/Http/Kernel.php 文件的 $routeMiddleware 属性中:
//scopes 中间件的可以简单理解成and,意思就是说在定义的路由的时候,要同时拥有指定的作用域才有权限
//scope 中间件可以简单理解成or, 意思就是说在定义的路由的时候,只要拥有一个指定的作用域就有权限
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
- 路由可以使用
scopes
中间件来检查当前请求是否拥有指定的 所有 作用域:
Route::get('/orders', function () {
// 访问令牌具有 "check-status" 和 "place-orders" 作用域...
//scopes 中间件的可以简单理解成and,scopes:check-status,place-orders意思就是说在定义的路由的时候,要同时拥有check-status和place-orders作用域才有权限
//scope 中间件可以简单理解成or, scope:check-status,place-orders意思就是说在定义的路由的时候,只要拥有check-status或者place-orders作用域就有权限
})->middleware(['auth:api', 'scopes:check-status,place-orders']);
补充事项:
提示:补充点
想起哪条再补充:
- 访问令牌的过期时间问题,oauth_access_tokens表是存放access_token的,里面有个过期时间字段expire_at,该字段只是记录该access_token的过期时间,我们有时候就想手动改这个时间,让access_token过期,这是不行的,因为过期时间在生成的时候就已经加密在返回的access_token里面了,生成后就不受数据表控制了,如果非要让该access_token失效就将revoked字段改为1
- passport oauth 有4种模式。这只是其中2种,后续再补充吧。