config/jwt.php
默认设置中,这个过期时间是一个小时,不过为了安全也可以设置更小一点,我设置了为五分钟。<?php
namespace App\Http\Middleware;
use App\Services\StatusServe;
use Closure;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
class CheckUserLoginAndRefreshToken extends BaseMiddleware
{
/**
* 检查用户登录,用户正常登录,如果 token 过期
* 刷新 token 从响应头返回
*
* @param $request
* @param Closure $next
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
* @throws JWTException
*/
public function handle($request, Closure $next)
{
/****************************************
* 检查 token 是否存在
****************************************/
$this->checkForToken($request);
try {
/****************************************
* 尝试通过 tokne 登录,如果正常,就获取到用户
* 无法正确的登录,抛出 token 异常
****************************************/
if ($this->auth->parseToken()->authenticate()) {
return $next($request);
}
throw new UnauthorizedHttpException('jwt-auth', 'User not found');
} catch (TokenExpiredException $e) {
try {
/****************************************
* token 过期的异常,尝试刷新 token
* 使用 id 一次性登录以保证此次请求的成功
****************************************/
$token = $this->auth->refresh();
$id = $this->auth
->manager()
->getPayloadFactory()
->buildClaimsCollection()
->toPlainArray()['sub'];
auth()->onceUsingId($id);
} catch (JWTException $e) {
/****************************************
* 如果捕获到此异常,即代表 refresh 也过期了,
* 用户无法刷新令牌,需要重新登录。
****************************************/
throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), null, StatusServe::HTTP_PAYMENT_REQUIRED);
}
}
// 在响应头中返回新的 token
return $this->setAuthenticationHeader($next($request), $token);
}
}
<?php
/*
* This file is part of jwt-auth.
*
* (c) Sean Tymon <tymon148@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Tymon\JWTAuth\Http\Middleware;
use Closure;
use Exception;
class Check extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->parser()->setRequest($request)->hasToken()) {
try {
$this->auth->parseToken()->authenticate();
} catch (Exception $e) {
}
}
return $next($request);
}
}
<?php
namespace App\Http\Middleware;
use Closure;
use Exception;
class Check extends BaseMiddleware
{
public function handle($request, Closure $next)
{
if ($this->auth->parser()->setRequest($request)->hasToken()) {
try {
$this->auth->parseToken()->authenticate();
} catch (TokenExpiredException $e) {
// 此处做刷新 token 处理
// 具体代码可以参考必须需要登录验证的接口
// 在响应头中返回新的 token
return $this->setAuthenticationHeader($next($request), $token);
} catch (Exception $e) {
}
}
return $next($request);
}
}
问题解决。 最后说一个并发会出现的问题:
# 当前 token_1 过期,先发起 a 请求,之后马上发起 b 请求
# a 请求到服务器,服务器判断过期,刷新 token_1
# 之后返回 token_2 给 a 请求响应
# 这时候迟一点的 b 请求用的还是 token_1
# 服务器已经将此 token_1 加入黑名单,所以 b 请求无效
token_1 刷新返回 token_2
a 请求 --------> server -------> 成功
token_1 过期的 token_1,应该使用 token_2
b 请求 --------> server ------> 失败
jwt-auth已经想到这种情况,我们只需要设置一个黑名单宽限时间即可
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.