3 JWT
1 概念
基于 token 的用户认证原理:让用户输入账号和密码,认证通过后获得一个token(令牌),在 token 有效期里用户可以带着 token 访问特定资源。
一开始 token 并没有一个统一标准,大家都各自使用自己的方案,后来出现了JWT( Json Web Token )这个标准。
2 JWT的结构
典型的JWT由三个部分组成,每个部分由一个点(.)分隔。
- Header
- Payload
- Signature
2.1 Header
头部包含所使用的签名算法和令牌的类型(即JWT),这部分会被编码为Base64URL格式。
{
"alg": "HS256",
"typ": "JWT"
}
2.2 Payload
Playload 包含实际要传输的信息,附带一些其他信息如过期时间、发行时间等。JWT 指定了一些官方字段(claims)备用,如下所示。
除了官方字段,在这个部分还可以添加自定义字段。
iss: 签发人
exp: 过期时间
iat: 签发时间
nbf: 生效时间
jti: 编号
sub: 主题
aud: 受众
2.3 Signature
Signature 部分是对前两部分的防篡改签名。将 Header 和 Payload 用 Base64URL 编码后,再用点(.)连接起来。然后使用签名算法和密钥对这个字符串进行签名。
signature = hmac_sha256(base64encode(header) + '.' + base64encode(payload), 'SECRET_KEY')
3 PHP实现
基于 tp5 框架,配置多套加密算法,对 payload 数据随机使用算法加密,保证数据安全。
$data 数据:
[
'header' => ['random' => rand(0, 2), 'type' => 'JWT'],
'payload' => ['data' => unique(), 'exp' => strtotime('+30 minutes', time())]
]
jwt.php 配置文件:
<?php
/**
* 参数说明:
* secret 密钥
* jwt_plan payload数据的加密方案
* - alg 算法
* - secrete 密钥
* - iv 向量。base64_encode()后的值,使用时需要 base64_decode()
* 使用 openssl_cipher_iv_length()、openssl_random_pseudo_bytes()生成
*/
return [
'secret' => '这里是填使用md5算法时的密钥',
'jwt_plan' => [
0 => [
'alg' => 'aes-128-cbc',
'secret' => '这里是填使用aes-128-cbc算法时的密钥',
'iv' => '这里是base64_encode后的iv向量',
],
1 => [
'alg' => 'aes-128-ccm',
'secret' => '这里是填使用aes-128-ccm算法时的密钥',
'iv' => '这里是base64_encode后的iv向量'
],
2 => [
'alg' => 'aes-256-ofb',
'secret' => '这里是填使用aes-256-ofb算法时的密钥',
'iv' => '这里是base64_encode后的iv向量'
]
],
];
生成 JWT:
private function jwtSignature($data)
{
$config = config('jwt.');
// 根据 random 值,获取对应的加密方案,对 payload 加密
$jwtPlan = $config['jwt_plan'][$data['header']['random']];
$payloadRaw = openssl_encrypt(
json_encode($data['payload']),
$jwtPlan['alg'],
$jwtPlan['secret'],
0,
base64_decode($jwtPlan['iv'])
);
// 字符+和/在URL中不能直接作为参数,把字符+和/分别变成-和_
$header = rtrim(strtr(base64_encode(json_encode($data['header'])), '+/', '-_'), '=');
$payload = rtrim(strtr($payloadRaw, '+/', '-_'), '=');
// 签名
$signature = md5($header.$payload.$config['secret']);
// 返回 JWT
return "{$header}.{$payload}.{$signature}";
}
验证 JWT:
private function jwtSignatureValidation($jwtStr)
{
try {
$config = config('jwt.');
list($headerRaw, $payloadRaw, $signature) = explode('.', $jwtStr);
// 校验签名
$toBeCheck = md5($headerRaw.$payloadRaw.$config['secret']);
if ($toBeCheck != $signature) return false;
// 解密 payload
$header = json_decode(base64_decode(strtr($headerRaw, '-_', '+/')), true);
$jwtPlan = $config['jwt_plan'][$header['random']];
$payload = json_decode(
openssl_decrypt(
strtr($payloadRaw, '-_', '+/'),
$jwtPlan['alg'], $jwtPlan['secret'],
0,
base64_decode($jwtPlan['iv'])
), true);
} catch (\Exception $exception) {
Log::record('jwtSignatureValidation_catch'.$exception->getMessage());
}
// 检查过期时间
return $payload['exp'] > time() ? $payload['data'] : false;
}
4 JWT怎么用
浏览器接收到服务器发过来的 jwt 后,可以存储在 Cookie 或 localStorage 中。之后,浏览器每次与服务器通信时都会带上 JWT。可以将 JWT 放在 Cookie 中,会自动发送(不跨域),或将 JWT 放在 HTTP 请求头的授权字段中,也可放在 URL 或 POST 请求的数据体中。
如果文章有帮到你的话,别忘了点赞收藏噢:smile: