laravel 使用dingo 和 jwt 时token过期自动刷新范例
本文章主要实现:用户token过期后自动获取新token并续订。然后提供了ajax的js代码。
1、先建立一个中间件文件:
1 |
php artisan make<span class="token punctuation">:</span>middleware RefreshJwtToken |
2、\app\Http\Middleware\RefreshJwtToken.php:文件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?php namespace App\Http\Middleware; use Auth; use Closure; use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\Http\Middleware\BaseMiddleware; use Tymon\JWTAuth\Exceptions\TokenExpiredException; use Tymon\JWTAuth\Http\Middleware\RefreshToken; use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; class RefreshJwtToken extends BaseMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { // 检查此次请求中是否带有 token,如果没有则抛出异常。 $this->checkForToken($request); // 使用 try 包裹,以捕捉 token 过期所抛出的 TokenExpiredException 异常 try { // 检测用户的登录状态,如果正常则通过 if ($this->auth->parseToken()->authenticate()) { return $next($request); } throw new UnauthorizedHttpException('jwt-auth', '未登录'); } catch (TokenExpiredException $exception) { // 此处捕获到了 token 过期所抛出的 TokenExpiredException 异常,我们在这里需要做的是刷新该用户的 token 并将它添加到响应头中 try { // 刷新用户的 token $token = $this->auth->refresh(); // 使用一次性登录以保证此次请求的成功 $sub = $this->auth->manager()->getPayloadFactory()->buildClaimsCollection() ->toPlainArray()['sub']; //可以加参数,来设定管理员和用户的不同guard. auth('api')->onceUsingId($sub); } catch (JWTException $exception) { // 如果捕获到此异常,即代表 refresh 也过期了, // 用户无法刷新令牌,需要重新登录。 throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage()); } } // 在响应头中返回新的 token return $this->setAuthenticationHeader($next($request), $token); } } |
3、在\app\Http\Kernel.php加上别名。
1 2 3 4 |
protected $routeMiddleware = [ ... 旧内容下新增 'refresh.token' => \App\Http\Middleware\RefreshJwtToken::class, //新增 ]; |
4、在\routes\api.php中设定中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); */ /* Laravel 路由缓存执行 php artisan route:cache 后,Dingo 报错该版本未注册路由 Dingo/api 的路由信息实际应该是单独的命令去进行缓存,也就是清理api路由缓存用php artisan api:cache */ $api = app('Dingo\Api\Routing\Router'); //middleware 可以在./config/api.php中配置 $api->version('v1',['namespace' => 'App\Http\Controllers\Api','middleware'=>[]], function ($api) { //namespace声明路由组的命名空间,因为设置了"prefix"=>"api",所以以下路由都要加一个api前缀, //比如请求/api/users_list才能访问到用户列表接口 //guest不需要验证的 #测试 $api->get("guest", 'RestFulController@guestTest')->name("getsite"); //登录 $api->post('user/login', 'AuthController@login'); //登录授权 //登录 $api->post('admin/login', 'AuthController@adminLogin'); //登录授权 //注销 $api->post('user/logout', 'AuthController@logout'); //注销授权 //注销 $api->post('admin/logout', 'AuthController@adminLogout'); //注销授权 //需要api验证的 $api->group(['middleware'=>['refresh.token','jwt.auth']], function ($api) { #测试 $api->get("user", 'RestFulController@userTest')->name("getsite"); }); //admin需要管理员登录的 $api->group(['middleware'=>['refresh.token','jwt.auth']], function ($api) { #测试 $api->get("admin", 'RestFulController@adminTest')->name("getsite"); }); }); |
5、在前端jquery ajax中获取新token并且使用(这个只是演示代码,写的尽量让人看懂原理。真正要用的话,尽量使用Promise来处理,并且最好写成对象或者方法来调用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
$(function () { var token = ""; var user = null; var user_id = 0; var user_name = "13800138000"; var password = "111111"; var accept = "application/vnd.API_SUBTYPE.v1+json" //vnd 是分支.API_SUBTYPE是evn dingo的配置,v1是版本号 //获取token $(document).on('click', '.btn-app .fa-edit', function (event) { var formData = {"user_name":user_name,"password":password}; $.ajax({ method: 'POST', url: "http://域名/api/user/login", headers: { Accept: accept }, data: formData, dataType:"json", success: function (data) { if(data.status_code == 10000) //10000是我自己定义的 { token = "Bearer "+data.data.token; user = data.data.user; user_id = user.id; } }, error:function(request){ //可以捕获status_code做错误处理,比如用户名验证错误、密码验证错误,errors是一个数组。 console.log(request.responseJSON); console.log(request.responseJSON.data); console.log(request.responseJSON.errors); console.log(request.responseJSON.status_code); console.log(request.responseJSON.message); } }); //阻止冒泡 event.preventDefault(); }); //获取用户数据 $(document).on('click', '.btn-app .fa-repeat', function (event) { var formData = {}; $.ajax({ method: 'GET', url: "http://域名/api/admin", headers: { Accept: accept Authorization: token }, data: formData, dataType:"json", success: function (data) { console.log(data); }, error:function(request){ console.log(request.responseJSON); }, complete: function (xhr, data) { //获取服务端自定义的header信息 var _token = xhr.getResponseHeader('Authorization'); //更新token if(token != null) { token = _token; } /* 获取相关Http Response header getResponseHeader(key):获取指定头信息 getAllResponseHeaders():获取全部可默认可获取的头信息 var date=xhr.getResponseHeader('Date');// 服务器端时间 var list = xhr.getAllResponseHeaders(); */ } }); //按钮回复正常。 event.preventDefault(); }); //真正过期后获取新token var new_token = function(user_name,password) { var new_token = {}; var formData = {"user_name":user_name,"password":password}; $.ajax({ method: 'POST', url: "http://域名/api/user/login", headers: { Accept: accept }, data: formData, dataType:"json", success: function (data) { if(data.status_code == 10000) { new_token.token = data.data.token; new_token.user = data.data.user; new_token.user_id = user.id; new_token.status_code = data.data.status_code; new_token.message = data.data.message; new_token.errors = null; } }, error:function(request){ new_token.status_code = responseJSON.status_code; new_token.message = request.responseJSON.message; new_token.errors = request.responseJSON.errors; new_token.token = null; new_token.user = null; new_token.user_id = null; } }); //阻止冒泡 event.preventDefault(); return new_token; } }); |
6、PS:\app\Http\Controllers\Api\AuthController.php 前端的验证控制器user/login代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public function login(Request $request) { $this->validate($request, [ 'user_name' => 'required|max:255', 'password' => 'required', ]); $data = []; try { //验证用户是否存在,存在则颁发token,不存在,则不颁发token。 if (! $token = auth('api')->attempt($request->only('user_name', 'password'))) { return $this->jsonResponse('用户不存在或者密码错误',$data,10002); } } catch (\TokenExpiredException $e) { //登录的时候,不可能存在token过期的。 return $this->jsonResponse('令牌过期',$data,10003); } catch (\TokenInvalidException $e) { return $this->jsonResponse('令牌无效',$data,10002); } catch (\JWTException $e) { return $this->jsonResponse('token_absent:'.$e->getMessage(),$data,500); } $data = [ 'user'=>auth('api')->user(), 'token'=>$token ]; return $this->jsonResponse('验证成功',$data); } |
噢!评论已关闭。