IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    如何在 WordPress 中生成和验证 JWT(JSON Web Token)

    Denis | 邓鸿华发表于 2023-06-16 07:28:25
    love 0

    好吧,终于在 #WPJAM Basic# 中实现了 JWT 登录和认证功能,今天就整理一下,首先按照惯例做一些 JWT 的介绍。

    什么是 JWT

    JWT(JSON Web Token)是一种轻量级的、自包含的、通常用于身份验证和授权的令牌,它将用户信息(如用户ID、角色和权限等)编码到一个 JSON 对象中,然后对其进行数字签名,从而生成一个经过签名的令牌。

    由于 JWT 在客户端和服务器之间传输时是加密的,因此服务器可以轻松验证该令牌的有效性和真实性。JWT 常用于无状态的 RESTful API,作为访问受保护资源的凭据,可以在每次请求时附加在请求头中,下图是使用 JWT 的一个常见的交互流程:

    JWT 的结构

    JWT(JSON Web Token)由三部分组成,分别是:Header(头部)、Payload(负载)和 Signature(签名)。

    这三部分使用点(.)分隔,合并为一个字符,如下所示:

    header.payload.signature

    Header(头部):

    Header 是一个 JSON 对象,通常包含两个属性:alg 和 typ,alg 属性表示签名算法(如 HS256、RS256 等),typ 属性表示令牌类型,通常为 "JWT"。Header 会被 Base64Url 编码,得到一个字符串。例如:

    {
      "alg": "HS256",
      "typ": "JWT"
    }

    Payload(负载):

    Payload 是一个 JSON 对象,包含一些“声明”(Claim),用于传递用户信息和其他业务数据,声明可以是预定义的(如 iss、exp、sub 等),也可以是自定义的,Payload 会被 Base64Url 编码,得到一个字符串。例如:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "iat": 1516239022
    }

    下面这 7 个字段都是由官方所定义的,也就是预定义(Registered claims)的,但并不都是必需的,

    • iss (issuer):签发人
    • sub (subject):主题
    • aud (audience):受众
    • exp (expiration time):过期时间
    • nbf (Not Before):生效时间,在此之前是无效的
    • iat (Issued At):签发时间
    • jti (JWT ID):唯一身份标识,主要用来作为一次性 token,从而回避重放攻击

    另外声明名称只有三个字符,因为 JWT 意味着是紧凑的。

    Signature(签名):

    签名用于保证 JWT 的完整性和安全性,它是将 Header、Payload 和一个密钥(Secret)通过签名算法进行加密得到的,签名可以防止 JWT 被篡改和伪造,例如,使用 HMAC-SHA256 算法生成签名的方法如下:

    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret
    )

    将 Header、Payload 和 Signature 用点(.)连接起来,就得到了一个完整的 JWT,例如:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

    在 WordPress 中实现 JWT

    通过上面 JWT 的介绍和 JWT 结构可知,生成 JWT,就是通过签名算法和一个用于生成签名的密钥(Secret)对 Payload 生成签名:我们为了方便就只支持 SHA256 签名算法:

    function wpjam_generate_jwt($payload, $secret='', $header=[]){
    	//无法生成没有设置过期时间的 JWT
    	if(empty($payload['exp'])){
    		return false;
    	}
    
    	$header	= wp_parse_args($header, [
    		'alg'	=> 'HS256',
    		'typ'	=> 'JWT'
    	]);
    
    	if($header['alg'] == 'HS256'){
    		$header		= base64_urlencode(wpjam_json_encode($header));
    		$payload	= base64_urlencode(wpjam_json_encode($payload));
    		$jwt		= $header.'.'.$payload;
    		$secret		= $secret ?: wp_salt();
    
    		return $jwt.'.'.base64_urlencode(hash_hmac('sha256', $jwt, $secret, true));
    	}
    }

    上面的代码首先对 Header 和 Payload 进行 JSON 编码和 URL 安全的 Base64 编码,生成签名的密钥(Secret)如果为空,则使用 WordPress 默认的盐值函数来生成,最后把 Header 和 Payload 和生成的签名通过点(.)连接起来得到了一个完整的 JWT 。

    那么怎么验证 JWT 呢,验证的过程就是生成的反过程:首先通过点(.)将 JWT 分割成 Header、Payload 和 Signature 三段,然后对 Header 和 Payload 进行 URL 安全的 Base64 解码和JSON 解码,通过签名的密钥最后验证签名:

    function wpjam_verify_jwt($token, $secret=''){
    	$tokens	= explode('.', $token);
    
    	if(count($tokens) != 3){
    		return false;
    	}
    
    	list($header, $payload, $sign) = $tokens;
    
    	$jwt		= $header.'.'.$payload;
    	$secret		= $secret ?: wp_salt();
    	$header		= wpjam_json_decode(base64_urldecode($header));
    	$payload	= wpjam_json_decode(base64_urldecode($payload));
    
    	if(empty($header['alg']) || $header['alg'] != 'HS256'){
    		return false;
    	}
    
    	if(!hash_equals(base64_urlencode(hash_hmac('sha256', $jwt, $secret, true)), $sign)){
    		return false;
    	}
    
    	//签发时间大于当前服务器时间验证失败
    	if(isset($payload['iat']) && $payload['iat'] > time()){
    		return false;
    	}
    
    	//该nbf时间之前不接收处理该Token
    	if(isset($payload['nbf']) && $payload['nbf'] > time()){
    		return false;
    	}
    
    	//没有设置过期时间,或过期时间小于当前服务器时间验证失败
    	if(empty($payload['exp']) || $payload['exp'] < time()){
    		return false;
    	}
    
    	return $payload;
    }

    我们这边生成和验证函数有点特殊处理,就是都要实现过期时间设置,意思就是不能设置没有过期时间的 JWT。

    wpjam_generate_jwt 和 wpjam_verify_jwt 这两个函数在 #WPJAM Basic# 中已经内置了,通过他们就可以在 WordPress 实现 JWT 的生成和验证了。

    特别提醒一下过程中用到的 URL 安全的 Base64 编码和解码函数 base64_urlencode 和 base64_urldecode 也是在 #WPJAM Basic# 中定义了。

    © WordPress果酱 / RSS 订阅 / 长期承接 WordPress 项目



沪ICP备19023445号-2号
友情链接