我的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。
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}}更新替换了。