文档章节

API 开发中可选择传递 token 接口遇到的一个坑

等月人
 等月人
发布于 06/24 12:24
字数 1328
阅读 96
收藏 1
JWt
  1. 在做 API 开发时,不可避免会涉及到登录验证,我使用的是jwt-auth
  2. 在登录中会经常遇到一个token过期的问题,在config/jwt.php默认设置中,这个过期时间是一个小时,不过为了安全也可以设置更小一点,我设置了为五分钟。
  3. 五分钟过期,如果就让用户去登录,这种体验会让用户直接抛弃你的网站,所以这就会使用到刷新token这个功能
  4. 正常情况下是写一个刷新token的接口,当过期的时候前端把过期的token带上请求这个接口换取新的token
  5. 不过为了方便前端也可以使用后端刷新返回,直至不可刷新,我用的就是这个方法:使用 Jwt-Auth 实现 API 用户认证以及无痛刷新访问令牌
  6. 而坑就是这样来的,
  • 在必须需要登录验证的接口设置刷新token
<?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);
    }
}
  • 而有些页面,比如文章列表页面,这个接口登录与不登录皆可访问,不过登录的时候可以在页面上显示是否点赞了这篇文章。所以这个接口直接使用的是jwt-auth默认的option中间件
<?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);
    }
}
  1. 一开始也没有发现问题,直到测试的时候,发现文章列表页面点赞过的文章,过了一段时间再刷新的时候发现不显示已点赞,但是进入个人中心的已点赞文章可以看到。
  2. 刚开始测试没找出原因,直接暴力调试代码,发现没获取到登录用户,一想不对呀,已经传token为何获取不到。经过发现,去到个人中心,再回到新闻列表页就可以正常显示,过了一段时间又不显示了。
  3. 经过这一轮之后,大概明白,在新闻列表页时,token已经过期,但是当时图方便用的jwt-auth默认的中间件,不会刷新token,所以这个接口获取不到登录的用户。当进入个人中心,发现当前token已经过期,后台刷新token返回,这时候再回到文章列表页就可以得到正常的数据,一段时间后,token又失效了,所以有无法看到点赞过的文章
  4. 解决方法,自己写一个option中间件,当存在token的时候,也需要做token刷新处理。
<?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已经想到这种情况,我们只需要设置一个黑名单宽限时间即可 我设置为5秒,就是当token_1过期了,你还能继续使用token_1操作5秒时间

原文地址

© 著作权归作者所有

共有 人打赏支持
等月人
粉丝 1
博文 12
码字总数 9745
作品 2
南宁
私信 提问
加载中

评论(2)

等月人
等月人

引用来自“松竹斋”的评论

你在过期的token中返回新的token 不会有问题么?

别人截取到你的已过期token ,发一次请求就可以获取到你的正常token,感觉安全隐患很大啊
过期的 token 是五分钟,如果五分钟过期了,用旧 token 换取新的 token 有条件的,如果都是已经旧的 token 请求换取新的 token。旧的 token 会被拉入黑名单。但并发会出现问题,所以旧的 token 可以设置几秒钟并发时间
松竹斋
松竹斋
你在过期的token中返回新的token 不会有问题么?

别人截取到你的已过期token ,发一次请求就可以获取到你的正常token,感觉安全隐患很大啊
微信支付2016开发调试过程记录

最近换了家公司,因为以前也是做支付的来这个公司直接让我从零开始搭建支付服务。首先是微信支付宝的第三方接入。我是做java开发的。支付宝有接入的sdk就是一个jar包。而且有沙箱环境测试帐号...

挨踢人生
2016/11/15
0
0
谈谈API版本控制的策略

上篇写《聊聊数据库和缓存同步机制》的时候,收到一份读者留言,希望我来谈谈API开发过程中的版本控制。这是一个很好的话题,对于任何互联网产品,随着需求的改进,都会遇到同样的问题,我自...

谢东升Forest
2017/07/26
0
0
七牛云模块化文档

前言 闲来无事,搭建了个人私仓来放模块化项目,以便项目引入直接使用。关键是,模块化可以根据项目所需进行版本迭代,每一次的使用都是完善的根基,效果贼棒。 介绍 七牛云是国内领先的企业...

Joryun刘家源
01/02
0
0
基于Koa.js的微信工具类小记

关于微信公众号开发和小程序开发的教程其实网上已经有很多了,但是基于koa.js 开发的教程其实不多,于是接下来对踩坑的经历做一些小结; 自己也写了一个微信工具类,项目地址是微信工具类 ...

evont
07/09
0
0
最近移动前端项目中遇到的两个坑

尽管要多加避免,但还别说,坑还真得常踩常有,不要以为手机端都是 Webkit 的天下就简单,不用处理浏览器之间的兼容问题,——其实不一致性的问题还是挺大的。 两个坑都是涉及 iframe 的——...

sp42
2016/01/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【Flutter教程】从零构建电商应用(一)

在这个系列中,我们将学习如何使用google的移动开发框架flutter创建一个电商应用。本文是flutter框架系列教程的第一部分,将学习如何安装Flutter开发环境并创建第一个Flutter应用,并学习Flu...

笔阁
15分钟前
1
0
什么是以太坊DAO?(三)

Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。 投票支付合约的所有费用和行动需要时间,并要求用户始终保持活跃,知情和专注。另一...

geek12345
17分钟前
0
0
一个本科学生对Linux的认知

一个本科学生对Linux的认知 我是一名大三的普通一本大学的软件工程的一名学生,学校开设了一些关于系统开发的课程,纸上得来终觉浅,学校的课程课时较短,想要在56个课时之内学会一些公司需要...

linuxCool
59分钟前
2
0
CentOS 安装Tomcat

Tomcat 介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 Java 程序写的网站用tomcat+jdk来运...

野雪球
今天
1
0
OSChina 周四乱弹 —— 每天都迟到是种什么样的体验

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @开源中国首席机器人 :《Too Good At Goodbyes (Acoustic) - Sam Smith - 单曲》 《Too Good At Goodbyes (Acoustic) - Sam Smith - 单曲》 ...

小小编辑
今天
709
12

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部