本文共 6420 字,大约阅读时间需要 21 分钟。
官网:https://jwt.io/
官网有案例测试。jwt-framework
github:https://github.com/web-token/jwt-framework 文档:https://web-token.spomky-labs.com/ composer require web-token/jwt-framework为什么要使用JWT
JWT是json web token缩写。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证。基于token的身份验证可以替代传统的cookie+session身份验证方法。它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。它具备两个特点:
简洁(Compact):可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快
自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
JWT由三个部分组成:header.payload.signature
1、header部分:
{ "typ": "JWT", "alg": "HS256"}
对应base64UrlEncode编码为:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
说明:该字段为json格式。alg字段指定了生成signature的算法,默认值为HS256,typ默认值为JWT2、payload部分:
{ "iss": "xxxx",//该JWT的签发者 "iat": 1569746556,//签发时间 "exp": 1601282556,//过期时间 "nbf": 1569746556,//该时间之前不接收处理该Token "jti": "NSiLlGKANgTRA9WD",//该Token唯一标识 "sub": xxxxxxx,//面向的用户,比如用户id "prv": "f6b71549db8c2c42b75827aa44f02b7ee529d24d"}
对应base64UrlEncode编码为:eyJpc3MiOiJodHRwOi8vMTcyLjMxLjIwNS43MC91Yy91Y2VudGVyL3JlQmluZGluZyIsImlhdCI6MTU2OTc0NjU1NiwiZXhwIjoxNjAxMjgyNTU2LCJuYmYiOjE1Njk3NDY1NTYsImp0aSI6Ik5TaUxsR0tBTmdUUkE5V0QiLCJzdWIiOjkzMjYwOSwicHJ2IjoiZjZiNzE1NDlkYjhjMmM0MmI3NTgyN2FhNDRmMDJiN2VlNTI5ZDI0ZCJ9
说明:该字段为json格式,表明用户身份的数据,可以自己自定义字段,很灵活。3、signature部分:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), 123456)
说明:对header和payload进行base64UrlEncode编码后进行拼接。通过key(这里是123456)进行HS256算法签名。
对应的签名为:wwTa9XRLydYmrjCOgiB4Wq1_rzGZ4sVeLg3NZVr0SaA4、最终得到的结果就是三段的拼接:header.payload.signature,即
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vMTcyLjMxLjIwNS43MC91Yy91Y2VudGVyL3JlQmluZGluZyIsImlhdCI6MTU2OTc0NjU1NiwiZXhwIjoxNjAxMjgyNTU2LCJuYmYiOjE1Njk3NDY1NTYsImp0aSI6Ik5TaUxsR0tBTmdUUkE5V0QiLCJzdWIiOjkzMjYwOSwicHJ2IjoiZjZiNzE1NDlkYjhjMmM0MmI3NTgyN2FhNDRmMDJiN2VlNTI5ZDI0ZCJ9.wwTa9XRLydYmrjCOgiB4Wq1_rzGZ4sVeLg3NZVr0SaA5、使用
一般将生成的jwt串放在请求头的 Authorization 中传递,服务端来验证。在使用 JWT 时需要注意以下事项:
代码:
'HS256', //生成signature的算法 'typ' => 'JWT' //类型 ]; // payload private $payload; // 过期时间 private $ttl = 10;// 10s private $refreshTtl = 7200; //使用HMAC生成信息摘要时所使用的密钥 private $key = '123456'; // 是否开启黑名单,开启后在校验token的时候会检查黑名单,影响性能,但是安全。 private $openBlackList = false; public function __construct() { } // 设置载荷 private function setPayload($pay = []) { $this->payload = [ "iss" => "jwt_admin",//该JWT的签发者 "iat" => time(),//签发时间 "exp" => time() + $this->ttl,//过期时间 "nbf" => time(),//该时间之前不接收处理该Token "jti" => md5(uniqid('JWT') . time()),//该Token唯一标识 "sub" => '',//面向的用户,比如用户id ]; $this->payload = array_merge($this->payload, $pay); } // 设置过期时间 public function setTtl($seconds) { $seconds = intval($seconds); if ($seconds > 0) { $this->ttl = $seconds; } } // 生成token public function getToken($payload) { $this->setPayload($payload); if (is_array($this->payload)) { $base64header = $this->base64UrlEncode(json_encode($this->header, JSON_UNESCAPED_UNICODE)); $base64payload = $this->base64UrlEncode(json_encode($this->payload, JSON_UNESCAPED_UNICODE)); $signature = $this->signature($base64header . '.' . $base64payload, $this->key, $this->header['alg']); $token = $base64header . '.' . $base64payload . '.' . $signature; return $token; } else { return false; } } // 验证token public function verifyToken($Token) { $tokens = explode('.', $Token); if (count($tokens) != 3) return false; list($base64header, $base64payload, $sign) = $tokens; //获取jwt算法 $base64decodeheader = json_decode($this->base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY); if (empty($base64decodeheader['alg'])) return false; //签名验证 if ($this->signature($base64header . '.' . $base64payload, $this->key, $base64decodeheader['alg']) !== $sign) return false; $payload = json_decode($this->base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY); // 判断黑名单 $payload['jti'] if($this->openBlackList){ } //签发时间大于当前服务器时间验证失败 if (isset($payload['iat']) && $payload['iat'] > time()) return false; //过期时间小宇当前服务器时间验证失败 if (isset($payload['exp']) && $payload['exp'] < time()) return false; //该nbf时间之前不接收处理该Token if (isset($payload['nbf']) && $payload['nbf'] > time()) return false; return $payload; } // 刷新token延长过期时间 public function refreshToken($token){ } /** * 当token泄露,或者注销登录,将token加入黑名单,使用redis存储,key的过期时间应大于token的过期时间。 * 一般将payload中的jti作为key,因为jti是唯一标识,value可以设置空,因为并不需要。 */ public function addBlackList(){ if($this->openBlackList){ } } /** * base64UrlEncode https://jwt.io/ 中base64UrlEncode编码实现 * 将 +/ 替换成 -_ * 将 = 删除 * @param string $input 需要编码的字符串 * @return string */ private function base64UrlEncode($input) { return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); } /** * base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现 * @param string $input 需要解码的字符串 * @return bool|string */ private function base64UrlDecode($input) { $remainder = strlen($input) % 4; if ($remainder) { $addlen = 4 - $remainder; $input .= str_repeat('=', $addlen); } return base64_decode(strtr($input, '-_', '+/')); } /** * HMACSHA256签名 https://jwt.io/ 中HMACSHA256签名实现 * @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload) * @param string $key * @param string $alg 算法方式 * @return mixed */ private function signature($input, $key, $alg = 'HS256') { $alg_config = array( 'HS256' => 'sha256' ); return $this->base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key, true)); }}// 测试加密$payload = array('sub' => '1234567890', 'name' => 'John Doe');$jwt = new Jwt;$token = $jwt->getToken($payload);echo $token.PHP_EOL;//对token进行验证签名$getPayload = $jwt->verifyToken($token);var_dump($getPayload);echo PHP_EOL;
转载地址:http://jgxui.baihongyu.com/