多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## JWT长什么样? JWT是由三段信息构成的,将这三段信息文本用`.`链接一起就构成了Jwt字符串。就像这样: ~~~ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ~~~ ## JWT的构成 ### 1. 头部(header), jwt的头部承载两部分信息: > * 声明类型[,这里是jwt > * 声明加密的算法 通常直接使用 HMAC SHA256 完整的头部就像下面这样的JSON: ~~~bash { 'typ': 'JWT', 'alg': 'HS256' } ~~~ 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分. ~~~ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ~~~ ### 2. 第二部分载荷(payload,), 载荷就是存放有效信息的地方。 >* 标准中注册的声明 >* 公共的声明 >* 私有的声明 **标准中注册的声明** (建议但不强制使用) : * **iss**: jwt签发者 * **sub**: jwt所面向的用户 * **aud**: 接收jwt的一方 * **exp**: jwt的过期时间,这个过期时间必须要大于签发时间 * **nbf**: 定义在什么时间之前,该jwt都是不可用的. * **iat**: jwt的签发时间 * **jti**: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 **公共的声明** : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密. **私有的声明** : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。 定义一个payload: ~~~json { "sub": "1234567890", "name": "John Doe", "admin": true } ~~~ 然后将其进行base64加密,得到Jwt的第二部分。 ~~~ eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ ~~~ ### playload 基本组成部分: > 简单点: ~~~ $payload=[ 'iss' => $issuer, //签发者 'iat' => $_SERVER['REQUEST_TIME'], //什么时候签发的 'exp' => $_SERVER['REQUEST_TIME'] + 7200 //过期时间 'uid'=>1111 ]; ~~~ > 复杂点:官方说法,三个部分组成(`Reserved claims`,`Public claims`,`Private claims`) ~~~ $token = [ #非必须。issuer 请求实体,可以是发起请求的用户的信息,也可是jwt的签发者。 "iss" => "http://maicaii.org", #非必须。issued at。 token创建时间,unix时间戳格式 "iat" => $_SERVER['REQUEST_TIME'], #非必须。expire 指定token的生命周期。unix时间戳格式 "exp" => $_SERVER['REQUEST_TIME'] + 7200, #非必须。接收该JWT的一方。 "aud" => "http://maicaii.com", #非必须。该JWT所面向的用户 "sub" => "admin", # 非必须。not before。如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟。 "nbf" => 50000, # 非必须。JWT ID。针对当前token的唯一标识 "jti" => '222we', # 自定义字段 "name" => "Gonggui", # 自定义字段 "Phone" => "13222222222", ]; ~~~ ### 3. 第三部分是签证(signature). jwt的第三部分是一个签证信息,这个签证信息由三部分组成: * header (base64后的) * payload (base64后的) * secret 这个部分需要base64加密后的header和base64加密后的payload使用`.`连接组成的字符串,然后通过header中声明的加密方式进行加盐`secret`组合加密,然后就构成了jwt的第三部分。 ~~~csharp // javascript var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ ~~~ 将这三部分用`.`连接成一个完整的字符串,构成了最终的jwt: ~~~csharp eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ~~~ **注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。** ### 如何应用 一般是在请求头里加入`Authorization`,并加上`Bearer`标注: ~~~bash fetch('api/user/1', { headers: { 'Authorization': 'Bearer ' + token } }) ~~~