JWT token 生成与验证
本文引用自https://jwt.io
JSON Web 令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地将信息作为 JSON 对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对对 JWT 进行签名。
尽管可以对 JWT 进行加密以提供双方之间的保密性,但我们将重点关注已签名的令牌。签名的令牌可以验证其中包含的声明的完整性,而加密的令牌则将这些声明隐藏在其他方的面前。当使用公钥/私钥对对令牌进行签名时,签名还证明只有持有私钥的一方才是对其进行签名的一方。
以下是 JSON Web 令牌有用的一些方案:
JWT 的结构
JSON Web 令牌以紧凑的形式由三部分组成,这些部分由点(.)分隔,分别是:
标头
有效载荷
签名
因此,JWT 通常如下所示。
xxxxx.yyyyy.zzzzz
让我们分解不同的部分。
标头
标头通常由两部分组成:令牌的类型(即 JWT)和所使用的签名算法,例如 HMAC SHA256 或 RSA。
例如:
{ "typ": "JWT", "alg": "RS256", "jti": "c04df38514f2ea7b3580f93f8d8845b69e17d8a70d0c5aae5cbc823f456c60773346fc142af5635d" }
typ 是 token 的类型
alg 是加密的算法
jti 是 jwt id
此 JSON 被 Base64Url 编码以形成 JWT 的第一部分。
有效载荷
令牌的第二部分是有效负载,其中包含声明。声明是有关实体(通常是用户)和其他数据的声明。
例如:
{ "aud": "2", "jti": "c04df38514f2ea7b3580f93f8d8845b69e17d8a70d0c5aae5cbc823f456c60773346fc142af5635d", "iat": 1614153428, "nbf": 1614153428, "exp": 1614758228, "sub": "1", "scopes": [] }
aud:Audience 代表谁拥有该 key,oauth2 里通常是 client_id
jti:jwt id
iat:issued at 时间戳
nbf:not valid before 时间戳
exp:过期时间戳
可能的值还有
iss:发行方
sub:主体内容
除了以上内容还可以包含自己的数据,例如name
,请不要包含敏感内容
对有效载荷进行 Base64Url 编码,以形成 JSON Web 令牌的第二部分。
签名
要创建签名部分,您必须获取编码的标头,编码的有效负载,秘钥,标头中指定的算法,并对其进行签名。
例如,如果要使用 RS256 算法,则将通过 openssl 私钥创建签名:
function JWT() { $time = time(); $header = ['alg' => 'RS256', 'typ' => 'JWT']; $claims = [ 'iss' => 'http://a.com', 'scope' => '*', 'aud' => 'http://b.com', 'exp' => $time + 3600, 'iat' => $time ]; $msg = Base64URLEncode(json_encode($header)) . '.' . Base64URLEncode(json_encode($claims)); $privateKey = "-----BEGIN PRIVATE KEY-----\nKEY\n-----END PRIVATE KEY-----\n"; $sign = ''; openssl_sign($msg, $sign, $privateKey, 'SHA256'); return $msg . '.' . Base64URLEncode($sign); } function Base64URLEncode($input) { return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); } echo JWT();
校验 JWT token
校验 jwt token 则可以使用 openssl 公钥进行验签,校验代码如下:
$jwt = "xxx.yyy.zzz"; function rsa_verify($sign,$str,$public_key){ $data = Base64URLDecode($sign); $publicKeyId = openssl_pkey_get_public($public_key); $result = openssl_verify($str, $data, $publicKeyId,"SHA256"); openssl_free_key($publicKeyId); return $result === 1 ? true : false; } $arr = explode(".",$jwt); $public_key = "-----BEGIN PUBLIC KEY-----\nKEY\n-----END PUBLIC KEY-----\n"; $is_verify = rsa_verify($arr[2].$arr[0].".".$arr[1],$public_key); if($is_verify){ echo "通过验证"; }else{ echo "未通过验证"; }
jwt 的加密方式不同,则校验方式也不相同!