前言:当某个项目需要前后端分离开发时,考虑到用户认证体系时,就可以使用jwt-auth认证。
目录
4. 注册两个 Facade (config/app.php)
一、jwt-auth的安装与配置
1. 使用 composer 安装 jwt-auth
# 建议使用1.0以上版本
composer require tymon/jwt-auth 1.*@rc
- 这里值得注意的是,有些文档会说要添加 Tymon\JWTAuth\Providers\LaravelServiceProvider::class ,这只在 Laravel 5.4 及以下版本是必要的,更新的 Laravel 版本无需添加。
- 还有一些文档说要添加 Tymon\JWTAuth\Providers\JWTAuthServiceProvider 这是很久以前的 JWT 版本的(大概 0.5.3 以前的版本)。
2.发布配置文件 和 生成秘钥
# 这条命令会在 config 下增加一个 jwt.php 的配置文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
# 这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar
php artisan jwt:secret
3. 更新需要Token认证的模型
如果你使用默认的 User 表来生成 token,你需要在该模型下增加一段代码
4. 注册两个 Facade (config/app.php)
这两个 Facade 并不是必须的,但是使用它们会给你的代码编写带来一点便利
'aliases' => [
...
// 添加以下两行
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
],
1、如果你不使用这两个 Facade,你可以使用辅助函数 auth ()
2、auth () 是一个辅助函数,返回一个 guard,暂时可以看成 Auth Facade。
3、对于它有很多有必要说的,可以参考:Laravel 辅助函数 auth 与 JWT 扩展详解https://learnku.com/articles/10889/detailed-implementation-of-jwt-extensions
// 如果你不用 Facade,你可以这么写
auth('api')->refresh();
// 用 JWTAuth Facade
JWTAuth::parseToken()->refresh();
两个 Facede 常用可使用方法,可以看文章后面的附录。
5. 修改 auth.php
// @TODO 为了更好的学习,我在这里新建了一个web_api的路由模块儿
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
//前后端分离接口
'web_api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
laravel如何配置自定义路由文件
1、首先在routes目录下创建你要添加的自定义路由文件
2、修改app/Providers目录下RouteServiceProvider.php文件
3、修改app/Http目录下Kernel.php文件
至此自定义路由文件配置完成
6. 注册路由(测试jwt-auth功能)
注意:在 Laravel 下,route目录下的路由文件,在实际接口路径中
默认都有前缀 api
。
Route::group(['prefix' => 'auth'], function ($router) {
Route::post('login', 'AuthController@login');
Route::post('logout', 'AuthController@logout');
Route::post('refresh', 'AuthController@refresh');
Route::post('me', 'AuthController@me');
});
7. 创建 token 控制器(测试jwt-auth功能)
php artisan make:controller AuthController
说明: AuthController.php 文件的内容主要还是针对web页面(前后端分离)使用的。
本人在测试过程中遇到的问题:
1、使用 auth( 'web_api' )->attempt( $credentials ) 验证 token时,用户的密码格式 必须是 bcrypt() 加密的格式,否则验证的结果是空。
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
/**
* 创建一个新的AuthController实例。
* 要求附带mobile和password(数据来源users表)
*
* @return void
*/
public function __construct()
{
// 这里额外注意了:官方文档样例中只除外了『login』
// 这样的结果是,token 只能在有效期以内进行刷新,过期无法刷新
// 如果把 refresh 也放进去,token 即使过期但仍在刷新期以内也可刷新
// 不过刷新一次作废
//$this->middleware('auth:api', ['except' => ['login']]);
// 另外关于上面的中间件,官方文档写的是『auth:api』
// 但是我推荐用 『jwt.auth』,效果是一样的,但是有更加丰富的报错信息返回
}
/**
* 通过给定的凭证获得JWT。
*
* @return \Illuminate\Http\JsonResponse
*/
public function login(Request $request)
{
$credentials = $request->only('mobile', 'password');
if(!$token = auth( 'web_api' )->attempt( $credentials )){
return response()->json( ['error' => 'Unauthorized'], 401 );
}
return $this->respondWithToken( $token );
}
/**
* 获取已验证的用户。
*
* @return \Illuminate\Http\JsonResponse
*/
public function me()
{
return response()->json( auth( 'web_api' )->user() );
}
/**
* 注销用户(使令牌失效)。
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth( 'web_api' )->logout();
return response()->json( ['message' => 'Successfully logged out'] );
}
/**
* Refresh a token.
* 刷新token,如果开启黑名单,以前的token便会失效。
* 值得注意的是用上面的getToken再获取一次Token并不算做刷新,两次获得的Token是并行的,即两个都可用。
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken( auth( 'web_api' )->refresh() );
}
/**
* 获取令牌数组结构。
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken( $token )
{
return response()->json( [
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth( 'web_api' )->factory()->getTTL() * 60
] );
}
}
二、JWT Token 详解
1. token 的获取、使用、删除和刷新
- 1.1 获取 token
- 1.2 使用 token
有两种使用方法:
- 加到 url 中:?token=你的token
- 加到 header 中,建议用这种,因为在 https 情况下更安全:Authorization:Bearer 你的token
添加中间件保护的就需要使用 token 进行访问
可以使用的中间件有 auth、auth:api、jwt.auth、jwt.refresh、jwt.check、jwt.renew
关于这些中间件之间有什么差别,可以看我的另一篇文章:Laravel 辅助函数 auth 与 JWT 扩展详解
- 1.3 删除 token
- 1.4 刷新 token
刷新token时需要注意的是: 通过 auth( 'web_api' )->logout() 操作之后的token,再去刷新时会报错,错误提示是:token 已被拉入黑名单,不能在刷新了。
2. token 的创建
前面的 AuthController.php 中有两行展现了这一种 token 的创建方法,即用用户所给的账号和密码进行尝试,密码正确则用对应的 User 信息返回一个 token 。
但 token 的创建方法不止这一种,接下来介绍 token 的三种创建方法:
- 基于账密参数
- 基于 users 模型返回的实例
- 基于 users 模型中的用户主键 id
- 基于账密参数
// 使用辅助函数
$credentials = request(['email', 'password']);
$token = auth()->attempt($credentials)
// 使用 Facade
$credentials = $request->only('email', 'password');
$token = JWTAuth::attempt($credentials);
- 基于 users 模型返回的实例
// 使用辅助函数
$user = User::first();
$token = auth()->login($user);
// 使用 Facade
$user = User::first();
$token = JWTAuth::fromUser($credentials);
- 基于 users 模型中的主键 id
// 使用辅助函数
$token = auth()->tokenById(1);
// 使用 Facade
源码中没找到
3. token 的解析
- 解析 token 到对象
// 把请求发送过来的直接解析到对象
JWTAuth::parseToken();
- 获取 token 中的 user 信息
// 辅助函数
$user = auth()->user();
// Facade
$user = JWTAuth::parseToken()->authenticate();
- 获取 token
如果 token 被设置则会返回,否则会尝试使用方法从请求中解析 token ,如果 token 未被设置或不能解析最终返回 false。
// 辅助函数
$token = auth()->getToken();
// Facade
$token = JWTAuth::parseToken()->getToken();
4. token 的三个时间
一个
token
一般来说有三个时间属性,其配置都在 config/jwt.php 内。
- 有效时间
有效时间指的的是你获得
token
后,在多少时间内可以凭这个token
去获取内容,逾时无效。
// 单位:分钟
'ttl' => env('JWT_TTL', 60)
- 刷新时间
刷新时间指的是在这个时间内可以凭旧 token 换取一个新 token。例如 token 有效时间为 60 分钟,刷新时间为 20160 分钟,在 60 分钟内可以通过这个 token 获取新 token,但是超过 60 分钟是不可以的,然后你可以一直循环获取,直到总时间超过 20160 分钟,不能再获取。
// 单位:分钟
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160)
- 宽限时间
宽限时间是为了解决并发请求的问题,假如宽限时间为 0s ,那么在新旧
token
交接的时候,并发请求就会出错,所以需要设定一个宽限时间,在宽限时间内,旧token
仍然能够正常使用。
// 宽限时间需要开启黑名单(默认是开启的),黑名单保证过期token不可再用,最好打开
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true)
// 设定宽限时间,单位:秒
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 60)
三、附录
1. JWT 的 两个 Facade
-
1.1 JWTAuth
JWTAuth::parseToken()->方法() 一般都可以换成 auth()->方法()。
- token 生成 attempt
根据 user 账密新建一个 token。
$credentials = $request->only('email', 'password');
$token = JWTAuth::attempt($credentials);
fromUser or fromSubject
根据 user 对象生成一个 token。后者是前者别名。
$user = User::find(1);
$token = JWTAuth::fromUser($user);
- token 控制 refresh 更新 token。
$newToken = JWTAuth::parseToken()->refresh();
invalidate 让一个 token 无效。
JWTAuth::parseToken()->invalidate();
check 检验 token 的有效性。
if(JWTAuth::parseToken()->check()) {
dd("token是有效的");
}
- token 解析
authenticate or toUser or user
这三个效果是一样的,toUser
是authenticate
的别名,而user
比前两者少一个 user id 的校验,但并没有什么影响。
$user = JWTAuth::parseToken()->toUser();
parseToken
从 request 中解析 token 到对象中,以便进行下一步操作。
JWTAuth::parseToken();
getToken
从 request 中获取 token。
// 这个不用 parseToken ,因为方法内部会自动执行一次
$token = JWTAuth::getToken();
jwt-auth官方参考链接:Quick start - jwt-auth