### 第一种 引用第三方 `composer require firebase/php-jwt` ~~~ <?php namespace app\service; use Firebase\JWT\JWT; use Firebase\JWT\Key; class JwtService { /** * 创建token * @param $login_id * @param $login_type * @param $exp * @param $ttl * @return string */ static function createToken($login_id,$login_type,$exp=7*24*3600,$ttl=24*3600): string { $key = getenv("TOKEN_KEY"); $payload = array( // "iss" => "http://example.org",//暂时用不到 // "aud" => "http://example.com",//暂时用不到 "iat" => time(), "nbf" => time(), "exp" => time()+$exp,//过期时间 "ttl" => time()+$exp+$ttl,//刷新时间 "login_id" => $login_id, "login_type" => $login_type, ); return JWT::encode($payload,$key,'HS256'); } /** * 解析token * @param $token * @return array */ static function analyseToken($token): array { $key = getenv("TOKEN_KEY"); $decode = JWT::decode($token, new Key($key, 'HS256')); //失效 if(time() > $decode->ttl){ return noticeMsg(300,"令牌失效"); } //过期 if(time() > $decode->exp){ $token = JwtService::createToken($decode->login_id,$decode->login_type); return noticeMsg(201,"令牌已刷新 请保存新令牌",[ 'login_id' => $decode->login_id, 'login_type' => $decode->login_type, 'token' => $token ]); } return noticeMsg(200,"success",[ 'login_id' => $decode->login_id, 'login_type' => $decode->login_type, ]); } } ~~~ ~~~ 标准声明:JWT标准规定的声明,但不是必须填写的; 标准声明字段: 接收该JWT的一方 iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: jwt的过期时间,过期时间必须要大于签发时间 nbf: 定义在什么时间之前,某个时间点后才能访问 iat: jwt的签发时间 ttl : 刷新时间 jti: jwt的唯一身份标识,主要用来作为一次性token。 其他声明:自己定义的字段,因为这部分是可以解开的,建议不要加入敏感信息,这里的data就是我自己定义的声明 ~~~ ~~~ public function jwt(Request $request) { $key = "wml123456789"; $payload = array( "iss" => "http://example.org", "aud" => "http://example.com", "iat" => time(), "nbf" => time(), "exp" => time()+2,//过期时间 "ttl" => time()+4//刷新时间 ); /** * IMPORTANT: * You must specify supported algorithms for your application. See * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 * for a list of spec-compliant algorithms. */ $jwt = JWT::encode($payload, $key, 'HS256');//生成 token try { $decoded = JWT::decode($jwt, new Key($key, 'HS256'));//验证token echo '<pre>'; var_dump($decoded); } catch(\Firebase\JWT\SignatureInvalidException $e) { //签名不正确 echo $e->getMessage(); }catch(\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用 echo $e->getMessage(); }catch(\Firebase\JWT\ExpiredException $e) { // token过期 echo $e->getMessage(); }catch(\Exception $e) { //其他错误 echo $e->getMessage(); } return 'end'; } ~~~ ### 手写的第二种 生成 token ~~~ $token = (new jwt())->aud('app')->sub($info->id)->ttl(7*24*60)->get_token(); ~~~ 校验 token ~~~ $jwt = new jwt(); $res = $jwt->parse_toekn($request);//检测token是否为空 if(is_array($res)) return response(errorMsg($res[1],$res[0])); $res = $jwt->check_sign();//检测token是否正确 if(is_array($res)) return response(errorMsg($res[1],$res[0])); $res = $jwt->check();//检测token是否过期 if(is_array($res)) return response(errorMsg($res[1],$res[0])); if($jwt->body_arr->aud != $source) return response(errorMsg(493,"token 验证失败!!")); // 将id挂载到request中 return ['login_id' => $jwt->body_arr->sub]; ~~~ jwt.php ~~~ <?php // 自定义jwt class jwt{ // 签名算法 private $alg = 'sha256'; // 令牌的类型 private $typ = 'JWT'; // 签发人 private $iss; // 主题 private $sub = 'token'; // 受众 private $aud; // 过期时间 private $exp; // 生效时间,在此之前是无效的 private $nbf; // 签发时间 private $iat; // 编号 private $jti; // 过期时间 private $ttl = 120; // 过期刷新时间 private $refresh_ttl = 240; // 头部数据 private $head; // 头部数组 public $head_arr; // 载体 private $body; // 载体数组 public $body_arr; // 签名 private $sign; // token public $token; // jwt秘钥 private $jwt_secret; // 生成头部 public function __construct(){ $this->jwt_secret = getenv('JWT_SECRET'); $this->ttl = getenv('JWT_TTL') ? getenv('JWT_TTL') : $this->ttl; $this->refresh_ttl = getenv('JWT_REFRESH_TTL') ? getenv('JWT_REFRESH_TTL') : $this->refresh_ttl; } // 设置过期时间 public function ttl($m){ $this->ttl = $m; return $this; } // 设置刷新时间 public function refresh_ttl($m){ $this->refresh_ttl = $m; return $this; } // 设置主题 public function sub($m){ $this->sub = $m; return $this; } // 设置受众 public function aud($m){ $this->aud = $m; return $this; } // 生成jwt头 protected function set_head(){ $this->head_arr = [ 'alg' => $this->alg, 'typ' => $this->typ, ]; $this->head = $this->base64url_encode(json_encode($this->head_arr)); } // 生成jwt载荷 protected function set_body(){ $this->iat = time(); $this->body_arr = [ 'sub' => $this->sub, 'aud' => $this->aud, 'iat' => $this->iat, 'exp' => $this->iat + $this->ttl * 60, ]; $this->body = $this->base64url_encode(json_encode($this->body_arr)); } // 设置签名 protected function set_sign(){ $this->set_head(); $this->set_body(); $this->sign = $this->base64url_encode(hash_hmac('sha256', $this->head . '.' . $this->body, $this->jwt_secret)); } public function check_sign(){ $sign = $this->base64url_encode(hash_hmac('sha256', $this->head . '.' . $this->body, $this->jwt_secret)); if($sign != $this->sign) return [493, 'token签名错误']; } // 设置token protected function set_token(){ $this->set_sign(); $this->token = $this->head . '.' . $this->body . '.' . $this->sign; } // 重置token protected function reset_token(){ $this->set_token(); return $this->token; } // 生成jwt public function get_token(){ if($this->token){ return $this->token; } return $this->reset_token(); } // 添加需要解析的token public function add_token($token){ $this->token = $token; return $this; } // 解析token public function parse_toekn($request){ $token = $request->token ?? ''; $token = $token ? $token : $this->token; if(!$token){ // 从header中获取 $app_token = $request->header('Authorization', ''); if(empty($app_token)){ return [493,"token格式不正确"]; } $token = explode(' ', $app_token); if(!isset($token[1])) return [493,"token不存在"]; $token = $token[1]; } $this->token = $token; $arr = explode('.', $token); $this->head = $arr[0]; $this->head_arr = json_decode(base64_decode($arr[0])); $this->body = $arr[1]; $this->body_arr = json_decode(base64_decode($arr[1])); $this->exp = $this->body_arr->exp; $this->sign = $arr[2]; // $this->check_sign(); return $this; } // 验证token有效期 public function check(){ // 验证token有效期 if($this->exp < time()){ return [493, 'token过期']; } return $this; } // 刷新token public function refresh_token(){ $this->can_refresh(); $this->sub = $this->body_arr->sub; $this->aud = $this->body_arr->aud; return $this->reset_token(); } // 是否能刷新token private function can_refresh(){ if(!$this->token){ return [493, 'token不存在']; } // 验证token能否刷新 if($this->exp < (time() - $this->refresh_ttl * 60)){ return [493, 'token刷新期已过']; } return $this; } // 生成base64 protected function base64url_encode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } } ~~~