01.JWT鉴权
1 什么是JWT
tip
JWT
是一种用于设备间数据传输的开放性标准的简洁独立的JSON
对象的证书。
由于使用数字签名,所以荷载的明文内容是不能被串改的, 一但串改将导致由密钥加密被窜改明文时生成的数字签名对不上附加在凭证上的签名而无效,
所以JWT
是一种明文加签名的的凭证, 明文内容使内容能被人查看,而签名使内容不能被窜改
2 JWT的组成(明文)
3部分组成,分别Header
,Payload
和signature
之间用.
分隔并base64
编码后反给用户。编码前格式大致'{"type":"JWT",...}.{"sub":"主题",...}.ABCDEFfsa!...'
。
.Header
参数
{
"typ": "JWT",
"alg": "HS256",
"jti": "aakfkd11"
}
参数 | 必有 | 说明 |
---|---|---|
typ | 是 | 令牌类型 |
alt | 是 | 算法类型 |
jti | 否 | JWT的编号也可放入payload |
.payload
参数(明文)
{
"iss": "http:\/\/a784admin.mxnt.net\/api\/authorizations",
"iat": 1569330469,
"exp": 1569330529,
"nbf": 1569330469,
"jti": "fASGUWtJfSgBOXLq",
"sub": 33,
"prv": "cb78b5e1ffce0f831d0231df2c8bd7c806477762"
}
参数 | 必有 | 说明 |
---|---|---|
iss | 否 | 【issuer】发布者的url地址 |
sub | 否 | 【subject】该JWT所面向的用户,用于处理特定应用,不是常用的字段 |
aud | 否 | 【audience】接受者的url地址 |
exp | 否 | 【expiration】 该jwt销毁的时间;unix时间戳 |
nbf | 否 | 【not before】 该jwt的使用时间不能早于该时间;unix时间戳 |
iat | 否 | 【issued at】 该jwt的发布时间;unix 时间戳 |
jti | 否 | 【JWT ID】 该jwt的唯一ID编号 |
tip
payload
的内容根据情况调整, 以上是官方的推荐参数.
tip
signture
是用于验证token
是否合法,没有被篡改的验证依据的字串。JWT
的生成算法有3种:
.对称加密 HMAC 【哈希消息验证码】
HS256/HS384/HS512
. 非对称加密 RSASSA【RSA签名算法】 和 ECDSA【椭圆曲线数据签名算法】
RS256/RS384/RS512
ES256/ES384/ES512
2 以下是采用HMAC
算法的PHP JWT
封装类
<?php
namespace App;
class Jwt
{
private $alg = 'sha256';
private $secret = "123456";
/**
* alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT
*/
public function getHeader()
{
$header = [
'alg' => $this->alg,
'typ' => 'JWT'
];
return $this->base64urlEncode(json_encode($header, JSON_UNESCAPED_UNICODE));
}
/**
* Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用,这里可以存放私有信息,比如uid
* @param $uid int 用户id
* @return mixed
*/
public function getPayload($uid)
{
$payload = [
'iss' => 'admin', //签发人
'exp' => time() + 600, //过期时间
'sub' => 'test', //主题
'aud' => 'every', //受众
'nbf' => time(), //生效时间
'iat' => time(), //签发时间
'jti' => 10001, //编号
'uid' => $uid, //私有信息,uid
];
return $this->base64urlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE));
}
/**
* 生成token,假设现在payload里面只存一个uid
* @param $uid int
* @return string
*/
public function genToken($uid)
{
$header = $this->getHeader();
$payload = $this->getPayload($uid);
$raw = $header . '.' . $payload;
$token = $raw . '.' . hash_hmac($this->alg, $raw, $this->secret);
return $token;
}
/**
* 解密校验token,成功的话返回uid
* @param $token
* @return mixed
*/
public function verifyToken($token)
{
if (!$token) {
return false;
}
$tokenArr = explode('.', $token);
if (count($tokenArr) != 3) {
return false;
}
$header = $tokenArr[0];
$payload = $tokenArr[1];
$signature = $tokenArr[2];
$payloadArr = json_decode($this->base64urlDecode($payload), true);
if (!$payloadArr) {
return false;
}
//已过期
if (isset($payloadArr['exp']) && $payloadArr['exp'] < time()) {
return false;
}
$expected = hash_hmac($this->alg, $header . '.' . $payload, $this->secret);
//签名不对
if ($expected !== $signature) {
return false;
}
return $payloadArr['uid'];
}
/**
* 安全的base64 url编码
* @param $data
* @return string
*/
private function base64urlEncode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
/**
* 安全的base64 url解码
* @param $data
* @return bool|string
*/
private function base64urlDecode($data)
{
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT))
}
}