关于 JSON Web Token 详细介绍请查阅资料
客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。 此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。 Authorization: Bearer 当跨域时,也可以将JWT被放置于POST请求的数据主体中
JWT的数据结构分三个部分:JWT头、有效载荷和签名;每个部分通过"."分隔符分为三个子串
1 JWT头 JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。 { “alg”: “HS256”, “typ”: “JWT” } 在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。 最后,使用Base64 URL算法将上述JSON对象转换为字符串保存
2 有效载荷 有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。 iss:发行人 exp:到期时间 sub:主题 aud:用户 nbf:在此之前不可用 iat:发布时间 jti:JWT ID用于标识该JWT 除以上默认字段外,我们还可以自定义私有字段,如下例: { “sub”: “1234567890”, “name”: “chongchong”, “admin”: true }
3 签名哈希 签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。 首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。 HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret) 在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。
正文 先用 composer 下载 官网:https://jwt.io/ 目录:app/common/auth/JwtAuth.php
namespace app\common\auth; use Lcobucci\JWT\Builder; use Lcobucci\JWT\Signer\Hmac\Sha256; use Lcobucci\JWT\Parser; use Lcobucci\JWT\ValidationData; class JwtAuth { /** * jwt token */ private $token; /** * jwt detoken */ private $decodetoken; /** * iss claim 参数 该JWT的签发者 */ private $iss = 'xxx'; /** * aud claim 参数 接收该JWT的一方 */ private $aud ='xxx'; /** * jti claim 参数 编号 */ private $jti = 'xxx'; /** * uid 参数 用户ID, */ private $uid; private $exp; /** * secrect key 密钥 */ private $secrect = 'xxxxxxx'; /** * 定义jwtAuth句柄 */ private static $instance; /** * 获取JwtAuth句柄 * @date 2019-04-15 */ public static function getInstance() { if(is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } /** * 私有化构造函数 * @date 2019-04-15 */ private function __construct() { } /** * 私有化克隆函数 * @date 2019-04-15 */ private function __clone() { } /** * 获取token * @date 2019-04-16 */ public function getToken() { return (string)$this->token; } /** * 设置token * @date 2019-04-16 */ public function setToken($token) { $this->token = $token; return $this; } /** * 设置用户ID * @date 2019-04-16 */ public function setUid($uid) { $this->uid = $uid; return $this; } /** * 获取用户ID * @date 2019-04-16 */ public function getUid() { return $this->uid; } /** * 获取Token有效期 * @date 2019-04-17 */ public function getTokenExp() { return $this->jwtDecode()->getClaim('exp'); } /** * 编码token * @param 有效期时间为当日+7天的凌晨 * @date 2019-04-16 */ public function jwtEncode() { $time = time(); // 有效期截止日期 $exp_sec = strtotime("+1 week",strtotime(date("Y-m-d 23:59:59"))); $this->token = (new Builder())->setHeader('alg','HS256') ->setIssuer($this->iss) ->setAudience($this->aud) ->setIssuedAt($time) ->setExpiration($exp_sec) // token 有效期 ->set('uid',$this->uid) ->set('openid',$this->openid) ->setId($this->jti, true) ->sign(new Sha256(), $this->secrect) ->getToken(); return $this; } /** * 解码token * @date 2019-04-16 */ public function jwtDecode() { if(!$this->decodetoken) { $this->decodetoken = (new Parser())->parse((string)$this->token); $this->uid = $this->decodetoken->getClaim('uid'); $this->openid = $this->decodetoken->getClaim('openid'); } return $this->decodetoken; } /** * 参数验证 * @date 2019-04-16 */ public function validation() { $data = new ValidationData(); $data->setIssuer($this->iss); $data->setAudience($this->aud); $data->setId($this->jti); return $this->jwtDecode()->validate($data); } /** * 签名验证 * @date 2019-04-16 */ public function verify() { return $this->jwtDecode()->verify(new Sha256(),$this->secrect); } }配合中间件使用
namespace app\http\middleware; use think\facade\Cache; use app\common\err\ApiErrCode; use app\common\exception\ApiException; use app\common\auth\JwtAuth; class AuthJwt { public function handle($request, \Closure $next) { $token = $request->param('token'); // 验证Token if($token) { $JwtAuth = JwtAuth::getInstance(); $JwtAuth->setToken($token); // 传递当前Token // 校验Token 参数和密钥 if($JwtAuth->validation() && $JwtAuth->verify()) { $uid = $JwtAuth->getUid(); $request->uid = $uid; // 获取当前用户ID return $next($request); }else{ // Token 无效/失效 throw new ApiException(ApiErrCode::ERROR_TOKEN); } }else{ throw new ApiException(ApiErrCode::ERROR_PARAM); } //return $next($request); } }