<?php
namespace app\common\library;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\ValidationData;
use Lcobucci\JWT\Parser;
use think\Exception;
class JWT
{
/**
* 现在的4.x是发行版 测试版
* 使用3.3.1 稳定版本
* composer require lcobucci/jwt 3.3.1
*/
/**
* 使用的加密类名称
* alg:
* 使用公钥私钥 加密对 请使用下面的类
* ['Lcobucci\JWT\Signer\Rsa\Sha256','Lcobucci\JWT\Signer\Rsa\Sha384','Lcobucci\JWT\Signer\Rsa\Sha512']
*
* 使用字符串密钥类【单向不可逆加密算法】 请使用下面的类
* ['Lcobucci\JWT\Signer\Hmac\Sha256','Lcobucci\JWT\Signer\Hmac\Sha384','Lcobucci\JWT\Signer\Hmac\Sha512']
*
* Ecdsa类 --- 在github上找了一个 BitcoinPHP/BitcoinECDSA.php[这个已经很完整了,可以单独使用] 所以不加进来
*/
private $alg = 'Lcobucci\JWT\Signer\Hmac\Sha256';
/**
* 用户 aud
*/
private $audience;
/**
* 身份id jti
*/
private $id;
/**
* 发布时间 iat
*/
private $issuedAt;
/**
* 发行人 iss
*/
private $issuer;
/**
* 主题 sub
*/
private $subject;
/**
* 到期时间 exp
*/
private $expiration;
/**
* 在此之前不可用 nbf
*/
private $notBefore;
/**
* 其他私有参数设置 比如uid
*/
private $claims = [];
/**
* 加密私钥 用于加密 使用[file://]+[文件路径] 比如根目录下的prikey.txt文件 eg:file://prikey.txt
*/
private $privateKey = null;
/**
* 解密公钥 如果 不使用 非对称加密类进行签名 那么公钥值等于私钥值
* 使用[file://]+[文件路径] 比如根目录下的pubkey.txt文件 eg:file://pubkey.txt
*/
private $publicKey = null;
public function __construct($config = [])
{
if (!is_array($config)) {
throw new Exception('构造参数必须是数组');
}
$time = time();
$this->alg = isset($config['alg']) && !empty($config['alg']) ? $config['alg'] : $this->alg;
$this->audience = isset($config['aud']) && !empty($config['aud']) ? $config['aud'] : get_ip();
$this->id = isset($config['jti']) && !empty($config['jti']) ? $config['jti'] : $this->getNoncestr(20);
$this->issuedAt = isset($config['iat']) && !empty($config['iat']) ? $config['iat'] : $time;
$this->issuer = isset($config['iss']) && !empty($config['iss']) ? $config['iss'] : $_SERVER['SERVER_NAME'];
$this->subject = isset($config['sub']) && !empty($config['sub']) ? $config['sub'] : '无主题';
$this->expiration = isset($config['exp']) && !empty($config['exp']) ? $time + $config['exp'] : $time + 3600;
$this->notBefore = isset($config['nbf']) && !empty($config['nbf']) ? $config['nbf'] : $time;
$this->privateKey = isset($config['privateKey']) && !empty($config['privateKey']) ? $config['privateKey'] : null;
$this->publicKey = isset($config['publicKey']) && !empty($config['publicKey']) ? $config['publicKey'] : null;
$this->claims = isset($config['claims']) && is_array($config['claims']) ? $config['claims'] : $this->claims;
}
/**
* 使用私钥加密token
*/
public function getToken()
{
$token = (new Builder())
->issuedBy($this->issuer)
->permittedFor($this->audience)
->identifiedBy($this->id, true)
->issuedAt($this->issuedAt)
->relatedTo($this->subject)
->canOnlyBeUsedAfter($this->notBefore)
->expiresAt($this->expiration);
if (count($this->claims) > 0) {
for ($i = 0; $i < count($this->claims); ++$i) {
$token->withClaim($this->claims[$i][0], $this->claims[$i][1]);
}
}
if (is_null($this->privateKey)) {
return $token->getToken();
}
$signer = new $this->alg;
$privateKey = new Key($this->privateKey);
return $token->getToken($signer, $privateKey);
}
/**
* 验证 token 的有效性
*/
public function verify($token)
{
if (is_null($this->publicKey)) {
return ['result' => false, 'errorMsg' => '解密密钥不能为空'];
}
$signer = new $this->alg;
$signer_key = $this->publicKey;
$token = (new Parser())->parse((string) $token);
$data = new ValidationData();
$data->setIssuer($token->getClaim('iss'));
$data->setAudience($token->getClaim('aud'));
$data->setId($token->getHeader('jti'));
$data->setSubject($token->getClaim('sub'));
//如果没有使用加密,那么就只验证数据
if ($token->getHeader('alg') !== 'none') {
//验证密钥是否匹配【公钥私钥】
if (!$token->verify($signer, $signer_key)) {
return ['result' => false, 'errorMsg' => '密钥不对'];
}
}
//验证token是否有效
if (!$token->validate($data)) {
return ['result' => false, 'errorMsg' => '密钥过期'];
}
return ['result' => true, 'msg' => '验证通过'];
}
/**
* 生成非对称密钥对
* 目的:方便自己生成密钥对,可以用于测试
*/
public function createRsaKey($alg = "sha256", $byte = 1024, $type = OPENSSL_KEYTYPE_RSA)
{
$config = array(
"digest_alg" => $alg,
"private_key_bits" => $byte, //512 1024 2048 4096
"private_key_type" => $type,
);
// 创建公钥和私钥 密钥对
$res = openssl_pkey_new($config);
//从新建的密钥对立面获取私钥
openssl_pkey_export($res, $privKey);
//从新建的密钥对立面获取公钥
$pubKey = openssl_pkey_get_details($res);
$pubKey = $pubKey["key"];
return ['result' => true, 'msg' => '非对称密钥对生成成功', 'publicKey' => $pubKey, 'privateKey' => $privKey];
}
/**
* 生成随机的字符作为本次请求的jti 识别id【标识】
* @param $length int
* 生成随机字符串
* 大于10位,将(当前时间戳+7200 ---- 作为有效时间)隔个字符插入
*/
private function getNoncestr($length = 20)
{
if ($length > 10) {
$strs = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm";
$str = substr(str_shuffle($strs), mt_rand(0, strlen($strs) - $length - 1 - 10), $length - 10);
$strArr = str_split($str, 1);
$timeArr = str_split(time() + 3600, 1);
$string = "";
if (count($strArr) < count($timeArr)) {
for ($i = 0; $i < count($timeArr); ++$i) {
if (isset($strArr[$i])) {
$string .= $strArr[$i] . $timeArr[$i];
} else {
$string .= $timeArr[$i];
}
}
} else {
for ($i = 0; $i < count($strArr); ++$i) {
if (isset($timeArr[$i])) {
$string .= $strArr[$i] . $timeArr[$i];
} else {
$string .= $strArr[$i];
}
}
}
} else {
$strs = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm";
$string = substr(str_shuffle($strs), mt_rand(0, strlen($strs) - $length - 1), $length);
}
return $string;
}
/**
* 设置属性
*/
public function __set($name, $val)
{
return $this->$name = $val;
}
/**
* 获取属性
*/
public function __get($name)
{
return $this->$name;
}
}
使用方式: //一般作为签名:那么就是分两步,一是生成令牌token,然后在前端使用,可以放到header或者作为参数[eg:$_GET] 二获取并验证token
//方式一 使用私钥 获取token Rsa 类
echo '<pre>';
$conf = [
'privateKey' => 'file://private.txt',
'alg' => 'Lcobucci\JWT\Signer\Rsa\Sha256',
'exp' => 30,
'sub' => 'ahai'
];
$list = new JWT($conf);
/**
* echo $token;
* example:
* eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjgxczV2Nzc3aDJnNTM5SzBONUM5In0.eyJpc3MiOiJ0ZXN0LmNtcy5uZXQiLCJhdWQiOiIxMC4wLjAuMTU1IiwianRpIjoiODFzNXY3NzdoMmc1MzlLME41QzkiLCJpYXQiOjE1NzcyNTU0NTksInN1YiI6ImFoYWkiLCJuYmYiOjE1NzcyNTU0NTksImV4cCI6MTU3NzI1OTA1OX0.dO0SiS6-85WSnz4SiBvb-BFR3oEVarwq6gWUvlZpMoId6w8WQ3Wx6WLlOSC8MWyn1ziapnhWP9usRfS5_3XicvzKI8fIilbFgHAhAJekxGXIOYw9TB66ggTuEQH4otLJH81hBwmfM4bJkd_N67kvh1HnpMMK3rdXIaiVWP9rKog
*/
$token = $list->getToken();
echo $token;
//方式一 使用公钥 解密token Rsa 类
echo '<pre>';
$conf = [
'publicKey' => 'file://public.txt',
'alg' => 'Lcobucci\JWT\Signer\Rsa\Sha256'
];
$lists = new JWT($conf);
var_dump($lists->verify($token));
//方式二 使用相同的密钥作为公私钥 Hmac 类
$conf = [
'privateKey' => '公钥与私钥一样',
'alg' => 'Lcobucci\JWT\Signer\Hmac\Sha256',
'exp' => 3600
];
$list = new JWT($conf);
$token = $list->getToken();
echo $token;
//方式二 使用相同的密钥作为公私钥 Hmac 类
$conf = [
'publicKey' => '公钥与私钥一样',
'alg' => 'Lcobucci\JWT\Signer\Hmac\Sha256'
];
$lists = new JWT($conf);
echo '<pre>';
var_dump($lists->verify($token));
//token是有headers + payload + signature 中间使用"."做为连接符号
//验证token
var_dump($lists->verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IngxeTU2N3M3STJUNWI5QzdTNU40In0.eyJpc3MiOiJ0ZXN0LmNtcy5uZXQiLCJhdWQiOiIxMC4wLjAuMTU1IiwianRpIjoieDF5NTY3czdJMlQ1YjlDN1M1TjQiLCJpYXQiOjE1NzcyNTYxNTQsInN1YiI6Ilx1NjVlMFx1NGUzYlx1OTg5OCIsIm5iZiI6MTU3NzI1NjE1NCwiZXhwIjoxNTc3MjU2MjE0fQ.-EhLYg0KlSyEMUdNJTAhk4-kcYvKr7G1_fwBWcuuEfs"));
ok,记录到这里。 最佳答案
