索引:
- 第一部分 基本概念
- 第二部分 使用流程
- 第三部分 实现代码
第一部分 基本概念
JWT就是一个字符串,经过加密处理与校验处理的字符串,由三个部分组成。基于token的身份验证可以替代传统的cookie+session身份验证方法。三个部分分别如下:
header.payload.signature
header部分:
{
"typ":"JWT",
"alg":"HS256"
}
这就是一个json串,两个字段都是必须的,alg字段指定了生成signature的算法,默认值为 HS256,可以自己指定其他的加密算法,如RSA.经过base64encode就可以得到 header.
payload部分:
$payload=[
'iss' => $issuer, //签发者
'iat' => $_SERVER['REQUEST_TIME'], //什么时候签发的
'exp' => $_SERVER['REQUEST_TIME'] + 7200 //过期时间
'uid'=>1111
];
signature部分
将 header和 payload使用header中指定的加密算法加密,当然加密过程还需要自定秘钥,自己选一个字符串就可以了。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
php实现代码:
<?php
public static function encode(array $payload, string $key, string $alg = 'SHA256')
{
$key = md5($key);
$jwt = self::urlsafeB64Encode(json_encode(['typ' => 'JWT', 'alg' => $alg])) . '.' . self::urlsafeB64Encode(json_encode($payload));
return $jwt . '.' . self::signature($jwt, $key, $alg);
}
public static function signature(string $input, string $key, string $alg)
{
return hash_hmac($alg, $input, $key);
}
将以上三个部分连起来就是JWT了。
参数解释
第二部分 JWT使用流程
实现步骤:
- 用户登录:输入用户名和密码
- 服务器进行用户名和密码的校验,通过之后生成JWT返回给前端
- 每次用户请求API的时候都带上JWT
- 服务端收到JWT做校验
第三部分 实现代码
首先composer安装一个JWT库,这里使用的是firebase的库,
composer require firebase/php-jwt
服务端在生成token时,加入少量的用户信息,比如用户的id。服务端接收到token之后,可以解析出这些数据,从而将token和用户关联了起来。
使用很简单,传入秘钥,生成Token:
private function generateToken($key){
vendor('JWT.JWT');
$key = $user_key;
$token = array(
"iss" => "https://feifei50.com", // Issued At Claim
"aud" => "https://feifei50.com", // Audience Claim
"iat" => $_SERVER['REQUEST_TIME'], // Issued At Claim
"nbf" => $_SERVER['REQUEST_TIME']-300, // Not Before Claim
"exp" => $_SERVER["REQUEST_TIME"]+3600 // Expire Time Claim
);
$jwt = JWT::encode($token, $key);
return $jwt;
}
返回的$jwt大概是这个样子:
//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvZmVpZmVpNTAuY29tIiwiYXVkIjoiaHR0cHM6XC9cL2ZlaWZlaTUwLmNvbSIsImlhdCI6MTUzNjI2OTM4NSwibmJmIjoxNTM2MjYxMDY1LCJleHAiOjE1MzYyNzk1NjV9.W19LwKH2eQbYs6YHKkbk7bOQK2dBfmV8jcNGIN4OGlY
key就是用来混淆的,自己随便定义好了,encode 需要key,decode 也需要key,这里用md5加密一下,会加入一个令牌混淆加密
在前端拿到这个token之后就将这个token放在LocalStroage中,每发送AJAX请求的时候都带上这个token给服务端,服务端解码这个token来做校验,如果校验没有通过,那么强制其跳回登录页面。
firebase的这个JWT会在解码的时候做部分的校验,比如key是否正确,token是否expired之类的,具体参考源码,很容易理解。以后后端每次拿到这组Token就要从Token中进行解码:
解码Token
private function decodeToken($jwt,$user_key){
JWT::$leeway = 300; // $leeway in seconds
$decoded = JWT::decode($jwt, $user_key, array('HS256'));
return $decoded;
}
这样解码之后就拿到了之前定义的payload的信息
本文作者熊冰,个人网站Bing的天涯路,转载请注明出处。