共计 3984 个字符,预计需要花费 10 分钟才能阅读完成。
我的 github 博客:https://zgxxx.github.io/
上一篇博客文章 https://segmentfault.com/a/11… 介绍了 laravel 使用 dingo+jwt 开发 API 的几个步骤,那么在实际操作中,我们需要测试 API
$api = app(‘Dingo\Api\Routing\Router’);
$api->version(‘v1’, [‘namespace’ => ‘App\Http\Controllers\V1’], function ($api) {
$api->post(‘register’, ‘AuthController@register’);
$api->post(‘login’, ‘AuthController@login’);
$api->post(‘logout’, ‘AuthController@logout’);
$api->post(‘refresh’, ‘AuthController@refresh’);
$api->post(‘me’, ‘AuthController@me’);
$api->get(‘test’, ‘AuthController@test’);
});
设置了这几个路由,对应的 url 类似这样:http://www.yourweb.com/api/me 使用 postman 来调试这些 API。
请求 API 的大致流程
我们使用 jwt 代替 session,首先是通过登录(jwt 的 attempt 方法验证账号密码),成功后会返回一个 JWT,我们把这个字符串统一叫做 token, 这个 token 需要我们客户端保存起来,然后后面需要认证的接口就在请求头里带上这个 token,后台验证正确后就会进行下一操作,如果 token 错误,或者过期就返回 401 或 500 错误,拒绝后面的操作。
前端可以保存在 localStorage, 小程序可以 使用 wx.setStorageSync 保存
所以请求头信息 Authorization:Bearer + token 很重要,但是有个问题,这个 token 是有一个刷新时间和过期时间的:’ttl’ => env(‘JWT_TTL’, 60),’refresh_ttl’ => env(‘JWT_REFRESH_TTL’, 20160),
refresh_ttl 是过期时间,默认 14 天,很好理解,就像一些网站一样,你好几个月没去登录,你的账号会自动退出登录,因为过期了,需要重新输入账号密码登录。
ttl 刷新时间默认 60 分钟,也就是说你拿这个一小时前的 token 去请求是不行的,会报 The token has been blacklisted 的错误,意思是说这个旧的 token 已经被列入黑名单,无法使用
token 是会被别人盗取的,所以 token 需要每隔一段时间就更新一次
这时候有个问题,每隔一小时就更新,那岂不是要每小时就重新登录一遍来获取新 token?当然不需要,我们可以写个中间件来实现无痛刷新 token, 用户不会察觉我们已经更新了 token。
<?php
namespace App\Http\Middleware;
use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class RefreshToken extends BaseMiddleware
{
/**
* @author: zhaogx
* @param $request
* @param Closure $next
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response|mixed
* @throws JWTException
*/
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();
// 使用一次性登录以保证此次请求的成功
\Auth::guard(‘api’)->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()[‘sub’]);
} catch (JWTException $exception) {
// 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
throw new UnauthorizedHttpException(‘jwt-auth’, $exception->getMessage());
}
}
return $next($request)->withHeaders([
‘Authorization’=> ‘Bearer ‘.$token,
]);
}
}
一旦中间件检测到 token 过时了,就自动刷新 token,然后在响应头把新的 token 返回来,我们客户端可以根据响应头是否有 ’Authorization’ 来决定是否要替换 token 在使用 postman 调试这些 API 的时候就有个问题,postman 又没有前端代码,我怎么及时更新这个 token, 难道每次请求都要去看响应头,发现 Authorization 后手动去复制黏贴吗,当然也不需要,postman 有个强大的环境变量,其实也是前端 js 的东西。
postman 自动刷新请求头 token
登录后自动获取 token
首先点击设置环境这个按钮,点击 Add 按钮添加一个变量,我们设置 key 值为 access_token,
接着我们在登录接口的 Tests 中去赋值这个变量
var data = JSON.parse(responseBody);
if (data.result.access_token) {
tests[“Body has token”] = true;
var tokenArray = data.result.access_token.split(” “);
postman.setEnvironmentVariable(“access_token”, tokenArray[1]);
}
else {
tests[“Body has token”] = false;
}
这段 js 代码就是获取请求成功后返回的 access_token 值,将其赋值给 postman 的环境变量,我们看到请求成功后我们后台返回了一个 json, 其中就有我们需要的 access_token,我们可以再去环境变量那里看看这时候的变量有什么变化
可以看到这里的变量 access_token 已经有值了,就是我们后台返回来的 access_token 字符串,说明赋值成功
接着我们到另一个需要认证的接口测试我们在 Authorization 类型 type 选择 Bearer Token, 在后面 Token 表单那里打一个 '{‘ 就会自动提示我们设置过的变量 {{access_token}} 发送请求测试下已经成功了。
无痛刷新 token
那如果 token 刷新了,经过后台中间件无痛刷新后,会在响应头返回一个新的 token(这一次请求用的是旧的 token, 默认认证通过)
现在我们需要在这个接口上直接更新我们的变量 access_token(如下图),而不需要去再请求一遍登录接口
var authHeader = postman.getResponseHeader(‘Authorization’);
if (authHeader){
var tokenArray = authHeader.split(” “);
postman.setEnvironmentVariable(“access_token”, tokenArray[1]);
tests[“Body has refreshtoken”] = true;
} else {
tests[“Body has no refreshtoken”] = false;
}
这段 js 代码就是将响应头中的 Authorization 赋值给我们的 access_token
这是响应头的 Authorization,截取了最后面的字符串
刷新时间过了后,我们试着再发一次请求,我们可以看到,还是可以访问的,而且请求头里的 Authorization 已经自动更新过来了
用一句话整理大概就是, 你需要在哪个接口响应后更新变量, 就去这个这个口的 Test 下写 js 赋值代码 postman.setEnvironmentVariable(“access_token”, token);,只要没错误你就可以在别的地方使用 {{access_token}} 更新替换了。