develop/jwt

JWT结合REDIS实现用户认证

v1.0.1 2022-01-28 09:02 UTC

This package is auto-updated.

Last update: 2025-06-13 15:07:10 UTC


README

介绍

jwt认证 https://jwt.io/

  • verifyJwt 验证jwt令牌, 项目中间件中引入
  • refreshJwt 刷新jwt令牌, 用旧jwt换取新的jwt,旧的jwt加入黑名单
  • creatToken 生成jwt令牌, 第一次生成jwt,不验证jwt
  • cancelJwt jwt加入黑名单

实际场景

前端

| 服务器生成token的过程中,会有两个时间,一个是token失效时间,一个是token刷新时间,刷新时间肯定比失效时间长,当用户的 token 过期时,你可以拿着过期的token去换取新的token,来保持用户的登陆状态,当然你这个过期token的过期时间必须在刷新时间之内,如果超出了刷新时间,依然返回登录失效, 请重新登录; 旧token换取之后, 会被服务端加入黑名单, 不能再使用;

前端实现无痛刷新token,流程大致为:

  • 在axios的拦截器中加入token刷新逻辑
  • 当用户token过期时,去向服务器请求新的 token
  • 把旧的token替换为新的token
  • 然后继续用户当前的请求
  • 完美的用户体验
后端

| 后端主动控制jwt失效, 引入redis黑名单

JWT注销黑名单膨胀的解决方案
  • 当注销或者刷新jwt, 将用户id作为key, 当前时间作为value, 有效时间为刷新时间减去当前时间戳; 这样的作用, 每个用户的黑名单条目数就从N个变成了1个;
  • 当api请求时, 先判断用户id是否在黑名单中存在; 若存在, 判断,jwt的签发时间是否小于黑名单的当前时间; 若小于, 则表示当前jwt已经加入黑名单了, 不能再使用; 这样查找范围就是未过期但又要注销的用户。
  • 未过期但要提前注销的用户或 token 数 < 所有已登录用户数 < 所有用户数,此处的『 < 』基本可以看成『远远小于』,所以黑名单策略虽然也算有状态,但是其维护的状态数也是特别小的。
JWT使用流程
  • verifyJwt 项目中间间中引入这个方法, 验证jwt合法性
  • jwt失效, 需要请求refreshJwt刷新令牌,就jwt加入黑名单
  • jwt提示加入黑名单, 需要重新登录
  • jwt超过最大刷新时间, 提示需要重新登录
  • jwt刷新, 不需要验证是否失效, 但其他情况必须验证, 刷新路由用refreshJwt这个路由名称,固定写死,以便核心代码不变动

使用说明

  1. 配置文件config.php说明 | 如果增加新模块, 需要和'api'模块配置一样增加配置文件,且和token实例化的模块名保持一致, demo的模块是'api',故配置也用'api'模块配置
return [
    #   配置api模块的key和过期时间,刷新时间
    'api' => [
        # HMAC生成信息摘要时所使用的密钥
        'key' => '202201271430@99999.com',
        # 过期时间,单位秒,默认10分钟
        'exp' => 10 * 60,
        # 刷新时间,单位秒, 默认3天
        'ref' => 3 * 24 * 60 * 60
    ]
];
  1. 使用
<?php
require_once "../vendor/autoload.php";

use Develop\Jwt\Jwt;

$config = require_once('./config.php');

$token = new Jwt($config, 'api');
$userId = 1;
# 生成jwt令牌
$tokenStr = $token->creatToken($userId);
# 验证jwt令牌
var_dump($token->refreshJwt($tokenStr));

uni-app下解码

| 相关依赖包: npm install --save base-64

getTokenInfo(){
    // 解码jwt
    let Authorization = 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsImlhdCI6MTY0MzMzOTI2MCwibmJmIjoxNjQzMzM5MjYwLCJleHAiOjE2NDMzMzk4NjAsInJlZiI6MTY0MzU5ODQ2MCwiYXVkIjoiYXBpIiwianRpIjoiOTlhYTE1ODI1OThjNTJkMmQzMDVjODY2MmE0MTQwNGIifQ.btXPDDJz0sHqvBTwEovJGcVzjGUmf8Lev_ItSvxmZsY'
    let tokenUser = Authorization.split(".")[1];
    let obj  = JSON.parse(base64.decode(tokenUser))
    console.log('jwt转换对象', obj)
    console.log('获取jwt刷新时间', obj.ref)       
}