一、JWT Token构成
头部Header . 载荷Payload . 签名Signature
1、头部 Header
声明了签名算法,如:['typ'=> 'JWT', 'alg' => 'HS256']
将以上数据定义成数组,再通过json_encode序列化,base64编码后得到头部信息。
2、载荷 Payload
即装载的数据,官方定义但不强制使用的数据字段:
| 12
 3
 4
 5
 6
 7
 
 | iss (issuer):签发人sub (subject):主题
 aud (audience):受众
 exp (expiration time):过期时间
 nbf (Not Before):生效时间,在此之前是无效的
 iat (Issued At):签发时间
 jti (JWT ID):编号
 
 | 
将以上数据定义成数组,再通过json_encode序列化,base64编码后得到载荷信息。
注意:
sub:为JWT所面向的用户,userid,是否使用是可选的;
jti:为 jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
3、签名 Signature
使用HMAC对头部+点+载荷生成信息摘要,即签名
二、JWT Token优缺点
优点:
- 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用
- 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息
- 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的
- 它不需要在服务端保存会话信息, 所以它易于应用的扩展
缺点(安全相关):
- 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分
- 保护好secret私钥,该私钥非常重要
- 如果可以,请使用https协议
三、基于tymon/jwt-auth包的源码实现
完整代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 
 | <?php
 
 
 
 
 $header = ['typ'=> 'JWT', 'alg' => 'HS256'];
 $header = base64UrlEncode(json_encode($header));
 
 
 
 
 
 
 
 
 
 
 
 $time = time();
 $userId = '7525571047194611';
 $claims = [
 'iss' => 'ipcloud-api',
 'iat' => $time,
 'exp' => intval($time) + 604800,
 'nbf' => $time,
 'jti' => sha1($userId.rand(100000, 999999)),
 'sub' => $userId,
 ];
 $payload = base64UrlEncode(json_encode($claims));
 
 
 $key = '7b21ff514564ea1be967ff7433485b00';
 
 
 
 $signature = hash_hmac('sha256', implode('.', [$header, $payload]), $key, true);
 $signature = base64UrlEncode($signature);
 
 
 $token = implode('.', [$header, $payload, $signature]);
 echo $token;
 
 
 
 
 
 
 
 
 function base64UrlEncode($data)
 {
 return str_replace('=', '', strtr(base64_encode($data), '+/', '-_'));
 }
 
 | 
Base64编码,是一种编码算法,不是加密算法,主要是为了将二进制数据转换成文本格式。
Base64编码的缺点是传输效率会降低,因为它把原始数据的长度增加了1/3。
因为标准的Base64编码会出现+、/和=,所以不适合把Base64编码后的字符串放到URL中。一种针对URL的Base64编码可以在URL中使用的Base64编码,它仅仅是把+变成-,/变成_
参考:https://www.liaoxuefeng.com/wiki/1252599548343744/1304227703947297
http://www.ruanyifeng.com/blog/2008/06/base64.html Base64编码原理
哈希算法又称摘要算法,是一种加密算法,主要是为了解决数据被篡改的问题。
使用哈希口令时,还要注意防止彩虹表攻击。
什么是彩虹表呢?如果只拿到MD5,从MD5反推明文口令,只能使用暴力穷举的方法。
然而黑客并不笨,暴力穷举会消耗大量的算力和时间。但是,如果有一个预先计算好的常用口令和它们的MD5的对照表:
![彩虹表]()
让彩虹表失效的做法就是:加盐(salt)
四、hash_hmac算法
Hmac算法是一种标准的基于密钥的哈希算法,可以配合MD5、SHA-1等哈希算法,计算的摘要长度和原摘要算法长度相同。
在php中,hash_hmac函数能将HMAC和一部分哈希加密算法结合起来实现HMAC-SHA1、HMAC-SHA256、HMAC-MD5等算法。函数介绍如下:
| 12
 3
 4
 5
 6
 
 | string hash_hmac(string $algo, string $data, string $key, bool $raw_output = false)algo:要使用的哈希算法名称,可以是上述提到的md5,sha1等
 data:要进行哈希运算的消息,也就是需要加密的明文。
 key:使用HMAC生成信息摘要是所使用的密钥。
 raw_output:该参数为可选参数,默认为false,如果设为true,则返回原始二进制数据表示的信息摘要,否则返回16进制小写字符串格式表示的信息摘要(注意是16进制数,而非简单的字母加数字)。
 另外:如果algo参数指定的不是受支持的算法,将返回false。
 
 | 
五、tymon/jwt-auth包关键文件或代码摘要
vendor\lcobucci\jwt\src\Signer\Hmac\Sha256.php //$signer签名者
vendor\lcobucci\jwt\src\Builder.php //Token构建
vendor\lcobucci\jwt\src\Signer\BaseSigner.php
vendor\lcobucci\jwt\src\Token.php
vendor\tymon\jwt-auth\src\Providers\JWT\Lcobucci.php
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 
 | 
 
 
 
 
 
 
 
 public function encode(array $payload)
 {
 
 $this->builder->unsign();
 
 try {
 foreach ($payload as $key => $value) {
 $this->builder->set($key, $value);
 }
 $this->builder->sign($this->signer, $this->getSigningKey());
 } catch (Exception $e) {
 throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
 }
 
 return (string) $this->builder->getToken();
 }
 
 
 
 
 
 
 
 
 
 
 public function decode($token)
 {
 try {
 $jwt = $this->parser->parse($token);
 } catch (Exception $e) {
 throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);
 }
 
 if (! $jwt->verify($this->signer, $this->getVerificationKey())) {
 throw new TokenInvalidException('Token Signature could not be verified.');
 }
 
 return (new Collection($jwt->getClaims()))->map(function ($claim) {
 return is_object($claim) ? $claim->getValue() : $claim;
 })->toArray();
 }
 
 | 
vendor\lcobucci\jwt\src\Builder.php
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | 
 
 
 
 public function getToken(Signer $signer = null, Key $key = null)
 {
 $signer = $signer ?: $this->signer;
 $key = $key ?: $this->key;
 
 if ($signer instanceof Signer) {
 $signer->modifyHeader($this->headers);
 }
 
 $payload = [
 $this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->headers)),
 $this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->claims))
 ];
 
 $signature = $this->createSignature($payload, $signer, $key);
 
 if ($signature !== null) {
 $payload[] = $this->encoder->base64UrlEncode($signature);
 }
 
 return new Token($this->headers, $this->claims, $signature, $payload);
 }
 
 | 
参考文章:
https://learnku.com/articles/10885/full-use-of-jwt
https://blog.csdn.net/mengzuchao/article/details/78473577
https://www.liaoxuefeng.com/wiki/1252599548343744/1255943717668160 加密与安全(廖雪峰)
http://www.ruanyifeng.com/blog/2008/06/base64.html Base64编码原理