参考文档:Laravel5.5源码详解--Auth中间件
[Laravel认证Auth,自定义driver,guard](https://learnku.com/articles/13434/the-laravel-framework-extends-auth-authentication-to-implement-custom-driver-guard)
[Laravel 辅助函数 auth 与 JWT 扩展详解](https://laravel-china.org/articles/10889/detailed-implementation-of-jwt-extensions)
一、安装JWT
1. 使用 composer 安装
# 使用1.0以上版本
composer require tymon/jwt-auth 1.*@rc
2. 进行一些配置
这里指的注意的是,有些文档会说要添加 Tymon\JWTAuth\Providers\LaravelServiceProvider::class
,这只在 Laravel 5.4 及以下版本是必要的,更新的 Laravel 版本无需添加。
还有一些文档说要添加 Tymon\JWTAuth\Providers\JWTAuthServiceProvider
这是很久以前的 JWT 版本的(大概0.5.3 以前的版本)。
2.1 发布配置文件
# 这条命令会在 config 下增加一个 jwt.php 的配置文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
2.2 生成加密密钥
# 这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar
php artisan jwt:secret
2.3 更新你的模型
如果你使用默认的 User 表来生成 token,你需要在该模型下增加一段代码
<?php
namespace App;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject # 这里别忘了加
{
use Notifiable;
// Rest omitted for brevity
/**
* Get the identifier that will be stored in the subject claim of the JWT.
* 获取将存储在JWT的中的标识符token。
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
* 返回一个键值数组,其中包含要添加到JWT中的任何自定义声明
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
上面两个方法,其实就是实现了JWTSubject接口必须实现的方法。这个也是按照官方文档进行配置即可。
2.4 注册两个 Facade
这两个 Facade 并不是必须的,但是使用它们会给你的代码编写带来一点便利。
config/app.php
'aliases' => [
...
// 添加以下两行
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
],
如果你不使用这两个 Facade,你可以使用辅助函数 auth(“api”)来替代JWTAuth::parseToken()。
2.5 修改 auth.php
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt', // 原来是 token 改成jwt
'provider' => 'users',
],
],
2.6 注册一些路由
注意:在 Laravel 下,route/api.php
中的路由默认都有前缀 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');
});
2.7 创建 token 控制器
php artisan make:controller AuthController
AuthController
值得注意的是 auth('api')
如果按照上面2.4配置了两个facade,可以使用JWTAuth::parseToken()替换auth('api');
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class AuthController extends Controller
{
/**
* Get a JWT via given credentials.
* 该方法用于生成token
* @return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* Get the authenticated User.
* 该方法通过token获取对应的用户信息
* @return \Illuminate\Http\JsonResponse
*/
public function me()
{
return response()->json(auth('api')->user());
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth('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('api')->refresh());
}
/**
* Get the token array structure.
*
* @param string $token
* 该方法按照指定的格式返回输出信息
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60
]);
}
}
二. token 的获取、使用、删除和刷新
- 以下用 postman 演示,
- Laravel 环境下写在 api.php 中的路由默认有前缀 api
1.1 获取 token
1.2 使用 token
有两种使用方法:
- 加到 url 中:
?token=你的token
- 加到 header 中,建议用这种,因为在 https 情况下更安全:
Authorization:Bearer 你的token
1.3 删除 token
删除 token
后,token就会失效,无法再利用其获取数据。
1.4 刷新 token
三、Token的刷新
如果 token
不刷新,那么 token
就相当于上面的用户名 + 密码,只要获取到了,就可以一直盗用,因此 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)
那么token在默认的60分钟后过期了怎么办呢?
创建一个中间件用来刷新token
art make:middleware RefreshToken
创建一个中间件用来刷新token
<?php
namespace App\Http\Middleware;
use Closure;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
class RefreshToken extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
//检查请求中是否带有token 如果没有token值则抛出异常
$this->checkForToken($request);
try {
//从请求中获取token并且验证token是否过期 若是不过期则请求到控制器处理业务逻辑 若是过期则进行刷新
if ($request->user = JWTAuth::parseToken()->authenticate()) {
return $next($request);
}
throw new UnauthorizedHttpException('jwt-auth', '未登录');
} catch (TokenExpiredException $exception) {
try {
//首先获得过期token 接着刷新token 再接着设置token并且验证token合法性
$token = JWTAuth::refresh(JWTAuth::getToken());
JWTAuth::setToken($token);
$request->user = JWTAuth::authenticate($token);
$request->headers->set('Authorization', 'Bearer ' . $token); // token被刷新之后,保证本次请求在controller中需要根据token调取登录用户信息能够执行成功
} catch (JWTException $exception) {
throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
}
}
//将token值返回到请求头
return $this->setAuthenticationHeader($next($request), $token);
}
public function checkForToken($request)
{
$token = $request->header('Authorization');
if (empty($token)) {
throw new UnauthorizedHttpException('jwt-auth', "未授权访问");
}
}
}
在Kernel
中注册中间件
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
......
'token.refresh' => \App\Http\Middleware\RefreshToken::class, //token.refresh、
];
在routes/api
中应用中间件
Route::post('auth/login', 'AuthController@login');
Route::middleware('token.refresh')->group(function () {
Route::post('auth/logout', 'AuthController@logout');
Route::post('auth/refresh', 'AuthController@refresh');
Route::post('auth/me', 'AuthController@me');
});
返回到header中新的token需要前端去监听,如果更换了前端每次请求中要换上这个新的token。
四、与Swoole一起用需要注意
swoole会缓存auth单例,开启swoole后,auth('api')会获取不到jwt驱动而报错,需要在每次路由请求前清除auth单例缓存,操作如下:
config/laravels.config中添加jwt服务提供者
'register_providers' => [
App\Providers\AuthServiceProvider::class,
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,//注册jwt提供者
\Illuminate\Session\SessionServiceProvider::class,
\Illuminate\Pagination\PaginationServiceProvider::class,
],
eventServiceProvides.php中清理auth单例
Event::listen('laravels.received_request', function (\Illuminate\Http\Request $req, $app) { Facade::clearResolvedInstance('auth'); }); parent::boot();
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!