来自 --- 找不到了,如果有人发现来源,请发个链接。
手写一遍 ,加深 印象。
使用tp框架,JWT写成了一个 中间件。
原理是php把一堆数据加密生成token给前段,前段请求接口带着这个token,php 解析token进行 验证。
需要在public/.htaccess加一行
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
1. 加密解密
/**
* 加密
* 将 +/ 替换成 -_
* 将 = 删除
*/
private function base64UrlEncode($input): string
{
return str_replace('=', '', strtr(base64_encode($input), ['+/' => '-_']));
}
/**
* 解密
* 关于 %4 百度一下 base64_encode
*/
private function base64UrlDecode($input)
{
$remainder = strlen($input) % 4;
if ($remainder) {
$addlen = 4 - $remainder;
$input .= str_repeat('=', $addlen);
}
return base64_decode(strtr($input, ['-_' => '+/']));
}
2. 生成签名
/**
* HMACSHA256 签名
*/
private function signature($input, $key, $alg = 'HS256')
{
$alg_config = [
'HS256' => 'sha256'
];
return $this->base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key, true));
}
3. 生成token之前的一些配置
// 定义头部
private $header = [
'alg' => 'HS256', // 生成signature的算法
'typ' => 'JWT' // 类型
];
// 统一时间
private $time = 0;
public function __construct()
{
$this->time = time();
}
// 需要加密的信息
private $payload;
// 过期时间
private $ttl = 10;
private $refreshTtl = 7200;
// 使用HMAC生成信息摘要时所使用的秘钥
private $key = '123456';
// 是否开启黑名单,开启后再校验token的时候会检查黑名单
private $openBlackList = false;
// 设置加密信息
private function setPayload(array $pay = [])
{
$this->payload = [
'iss' => 'jwt_admin', // 该JWT的签发者
'iat' => $this->time, // 签发时间
'exp' => $this->time, // 过期时间
'nbf' => $this->time, // 改时间之前不接受处理该token
'jti' => md5(uniqid('JWT') . $this->time), // 该token的唯一标识
'sub' => '1', // 面向的用户,比如用户id
];
$this->payload = array_merge($this->payload, $pay);
}
// 设置过期时间
public function setTtl(int $seconds)
{
if ($seconds > 0) {
$this->ttl = $seconds;
}
}
4. 生成 token
/**
* 生成token
*/
public function createToken(array $payload)
{
$this->setPayload($payload);
if (is_array($this->payload)) {
$base64header = $this->base64UrlEncode(json_encode($this->header, JSON_UNESCAPED_UNICODE));
$base64payload = $this->base64UrlEncode(json_encode($this->payload, JSON_UNESCAPED_UNICODE));
$signature = $this->signature($base64header . '.' . $base64payload, $this->key, $this->header['alg']);
$token = $base64header . '.' . $base64payload . '.' . $signature;
return $token;
} else {
return false;
}
}
5. 验证token
/**
* 验证token
*/
public function verifyToken($token)
{
$token1 = explode(' ', $token);
if (count($token1) !== 2) {
return false;
}
$tokens = explode('.', $token1[1]);
if (count($tokens) !== 3) {
return false;
}
// dd($tokens);
list($base64header, $base64payload, $sign) = array_map(function($v){ return trim($v);},$tokens);
// 获取jwt算法
$base64decodeheader = json_decode($this->base64UrlDecode($base64header), true);
if (empty($base64decodeheader['alg'])) {
return false;
}
// 签名验证
if ($this->signature($base64header . '.' . $base64payload, $this->key, $base64decodeheader['alg']) !== $sign) {
return false;
}
$payload = json_decode($this->base64UrlDecode($base64payload), true);
// 判断黑名单 $payload['jti']
if ($this->openBlackList) {
// return true;
}
// 签发时间大于当前服务器时间验证失败
if (isset($payload['iat']) && $payload['iat'] > $this->time) {
// return false;
}
// 过期时间小于当前服务器时间验证失败
if (isset($payload['exp']) && $payload['exp'] < $this->time) {
// return false;
}
// 该nbf时间之前不接收处理该token
if (isset($payload['nbf']) && $payload['nbf'] > $this->time) {
// return false;
}
return $payload;
}