jwt

本文最后更新于:2023年12月5日 晚上

https://www.easyswoole.com/Components/jwt.html
https://baijiahao.baidu.com/s?id=1608021814182894637&wfr=spider&for=pc
使用 JWT 实现单点登录(完全跨域方案)

JWT:JSON Web Token

JWT 不仅可用于认证,还可用于信息交换。善用 JWT 有助于减少服务器请求数据库的次数

生成 token 编码

用户信息 + 固定数据(加密方式、用户、过期时间等)+ 密钥 加密,得到 签名,然后将这四部分数据 base64 编码,生成一个长字符串,这个长字符串就是 token

注意:

  • 密钥保存在服务器上,千万不能泄露,可以设置成 项目名称+随机字符串 的格式
  • token 中的所有信息都是公开的,所以一定不能存放密码等隐私信息
  • 一旦 JWT 签发,有效期内将会一直有效,不能取消 token 或更改 token 的权限为了减少盗用,JWT 的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证
  • 密钥可以保证 token 的安全性,但是无法防止 token 的盗用问题,为了减少盗用和窃取,推荐使用 HTTPS 协议进行传输

解析 token 解码

base64 解码 token,得到一堆明文信息和签名,明文信息里有一个字段说明了加密用的算法,将明文信息使用指定的加密方式加密,如果等于签名,则 token 校验通过,在进行超时等其他参数的校验

easyswoole 中的 jwt

官方文档:https://www.easyswoole.com/Components/jwt.html

官方文档写的比较简略,通过查阅源码,可以有更详细的了解。

token 由三或四部分组成:

  1. 前缀:prefix,可省略

  2. 头部:header,是 alg(加密方式)和 typ(类型,默认 JWT 且无法更改)的组合,经过 base64 转码

  3. 有效载荷:payload,包含以下信息,经过 base64 转码

    'exp' => $this->getExp(),   // 过期时间,默认两个小时
    'sub' => $this->getSub(),   // 主题,没有默认值
    'nbf' => $this->getNbf(),   // 在此之前不可用,默认time()
    'aud' => $this->getAud(),   // 用户,无默认值
    'iat' => $this->getIat(),   // 发布时间,默认time()
    'jti' => $this->getJti(),   // jwt-id,用于标识jwt,默认10字节的随机字符串
    'iss' => $this->getIss(),   // 发行人,默认'EasySwoole'
    // token状态,0初始状态,1合法token,-1非法token,-2过期token,生成token的时候,status默认给1
    // 不太理解payload中为什么要包含这个字段
    'status' => $this->getStatus(),
    'data' => $this->getData()   // 自定义的数据

    必要的信息都有默认值:

    // Jwt
    private $secretKey = 'EasySwoole';
    private $alg = Jwt::ALG_METHOD_HS256; // 默认加密方式
    
    // JwtObject
    protected $iss = 'EasySwoole'; // 发行人
    
    if (empty($this->nbf)) {
        $this->nbf = time();
    }
    if (empty($this->iat)) {
        $this->iat = time();
    }
    if (empty($this->exp)) {
        $this->exp = time() + 7200;
    }
    if (empty($this->jti)) {
        $this->jti = Random::character(10);
    }
  4. 签名:signature,包含 secretKey(秘钥)、header、payload、alg,经过加密然后 base64 转码

    除了 secretKey,其他所有信息相当于都是明文,secretKey 储存在服务器的内存中,如果重启服务,secretKey 就会丢失,之前的 token 就都无法通过校验,所以推荐直接修改 Jwt 组件中的默认 secretKey,然后在生成 token 的时候,不指定 secretKey,使用默认值

生成 token,以下是最简略的写法:

use EasySwoole\Jwt\Jwt;

$token = Jwt::getInstance()->publish()->__toString();

生成 token,以下是推荐写法:

use EasySwoole\Jwt\Jwt;

$jwtObject = Jwt::getInstance()->publish();
$jwtObject->setPrefix('Bearer');    // token前缀,Bearer表示标准jwt格式
$jwtObject->setSub('yuanzhan');    // 主题
$jwtObject->setAud($userId);    // 用户id
$jwtObject->setExp(time() + 3600);    // 过期时间设置为1个小时
// 自定义数据
$jwtObject->setData([
    'other_info'
]);

$token = $jwt->__toString();

自动续期

设置 token 过期时间 2h,2h 内用户一直在操作,然后贸然过期肯定不合理,所以 token 要有自动续期机制。

方案:

  • 后端:每次校验 token 的过期时间,如果过期但是过期时间不超过 4 个小时,则生成一个新 token,并附着在此次请求返回的数据的基础上。这里用“refreshToken”字段
  • 前端:每次 http 请求校验返回值中有没有“refreshToken”字段,如果有,则刷新本地 token

jwt
http://blog.lujinkai.cn/PHP/EasySwoole/jwt/
作者
像方便面一样的男子
发布于
2020年12月11日
更新于
2023年12月5日
许可协议