Skip to main content

01.JWT鉴权

1 什么是JWT

tip

JWT是一种用于设备间数据传输的开放性标准的简洁独立的JSON对象的证书。 由于使用数字签名,所以荷载的明文内容是不能被串改的, 一但串改将导致由密钥加密被窜改明文时生成的数字签名对不上附加在凭证上的签名而无效, 所以JWT是一种明文加签名的的凭证, 明文内容使内容能被人查看,而签名使内容不能被窜改

2 JWT的组成(明文)

3部分组成,分别Header,Payloadsignature之间用.分隔并base64编码后反给用户。编码前格式大致'{"type":"JWT",...}.{"sub":"主题",...}.ABCDEFfsa!...'。 .Header参数

{
"typ": "JWT",
"alg": "HS256",
"jti": "aakfkd11"
}
参数必有说明
typ令牌类型
alt算法类型
jtiJWT的编号也可放入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))
}
}