JWT由三个部分组成:header.payload.signature
1 2 3 4
| { "alg": "HS256", "typ": "JWT" }
|
对应base64UrlEncode编码为:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
说明:该字段为json格式。alg字段指定了生成signature的算法,默认值为 HS256,typ默认值为JWT
payload部分:
1 2 3 4 5
| { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
|
对应base64UrlEncode编码为:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
说明:该字段为json格式,表明用户身份的数据,可以自己自定义字段,很灵活。sub 面向的用户,name 姓名 ,iat 签发时间。例如可自定义示例如下:
1 2 3 4 5 6 7 8 9
| {
"iss": "admin", "iat": 1535967430, "exp": 1535974630, "nbf": 1535967430, "sub": "www.admin.com", "jti": "9f10e796726e332cec401c569969e13e" }
|
signature部分:
1 2 3 4 5
| HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), 123456 )
|
对应的签名为:keH6T3x1z7mmhKL1T3r9sQdAxxdzB6siemGMr_6ZOwU
最终得到的JWT的Token为(header.payload.signature):eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.keH6T3x1z7mmhKL1T3r9sQdAxxdzB6siemGMr_6ZOwU
说明:对header和payload进行base64UrlEncode编码后进行拼接。通过key(这里是123456)进行HS256算法签名。
JWT使用流程
- 初次登录:用户初次登录,输入用户名密码
- 密码验证:服务器从数据库取出用户名和密码进行验证
- 生成JWT:服务器端验证通过,根据从数据库返回的信息,以及预设规则,生成JWT
- 返还JWT:服务器的HTTP RESPONSE中将JWT返还
- 带JWT的请求:以后客户端发起请求,HTTP REQUEST
- HEADER中的Authorizatio字段都要有值,为JWT
- 服务器验证JWT
相比token而言
相比token而言使用方式上和服务器的开销上有区别,原始的token在用户登录后(api同理)会给前端返回一个token同时服务器也需要存储这个token,前端在请求业务接口时会携带这个token,而服务器需要每次进行对token进行校验、数据库查询,检验是否合法;
而JWT的方式与token有一定区别,最大的区别在于前端每次请求后端携带的token服务端是不需要存储的,合法校验这一步换成通过JWT去校验,从而减少服务端的开销与资源浪费
JWT-php依赖的引入
1
| composer require firebase/php-jwt
|
方法封装
1 2 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 55 56 57 58 59 60 61 62 63 64 65 66 67
| <?php
namespace app\common\service;
use DomainException; use Firebase\JWT\ExpiredException; use Firebase\JWT\JWT; use Firebase\JWT\Key; use Firebase\JWT\SignatureInvalidException; use InvalidArgumentException; use UnexpectedValueException;
class JWTService { const PRIVATE_KEY = "KOWl3yJFuB5K3QG9Vae6wCiCgenirgWC";
public function jwt_encode( array $payload, int $expire_time = 0, string $key = self::PRIVATE_KEY, string $alg = "HS256" ): string { if ($expire_time > 0) { $payload['exp'] = $expire_time; } return JWT::encode($payload, $key, $alg); }
public function jwt_decode($token, string $key = self::PRIVATE_KEY, string $alg = "HS256"): array { try { $decoded_arr = JWT::decode($token, new Key($key, $alg)); return [true, "处理成功", $decoded_arr]; } catch (InvalidArgumentException $e) { return [false, "参数错误:".$e->getMessage()]; } catch (DomainException $e) { return [false, "算法错误:".$e->getMessage()]; } catch (SignatureInvalidException $e) { return [false, "签名错误:".$e->getMessage()]; } catch (ExpiredException $e) { return [false, "签名已过期:".$e->getMessage()]; } catch (UnexpectedValueException $e) { return [false, "异常:".$e->getMessage()]; } } }
|
使用
1 2 3 4 5 6 7 8 9 10 11
| $payload = [ 'user_name' => "xiexiang", 'password' => "123456", 'sex' => 1, 'age' => 2, 'exp' => 1692063675 ];
$token = (new JWTService())->jwt_encode($payload); [$status, $msg, $data] = (new JWTService())->jwt_decode($token); var_dump($status, $msg, (array)$data);die;
|