搜索
系统检测到您的用户名不符合规范:

api接口数据-验证-整理【续】

浏览:1851 发布日期:2019年12月25日 分类:ThinkPHP5专区 关键字: jwt JWT api thinkphp5 lcobucci/jwt
这3-4个月接触微信公众号开发比较多,偶尔需要做一些接口【内部使用】,我的上一篇文章写了:ssl协议,白名单[ip,域名,url],黑名单[ip,域名,url],请求频率[eg:每10秒,最大请求数],设备【电脑,手机】,终端浏览器[微信,qq,...],设置请求方式并禁用curl获取数据[保证用户只能在页面使用ajax请求];差了一个签名验证的方式,本来想做一微信的JS-SDK签名方式类似的东西;js-sdk:通过服务端生成签名,然后js前端将签名值和参与签名的参数 传递 给接口,接口进行验证 返回数据。如果模仿微信的做法,那么我好像还需要做一个获取token 和 ticket的接口,感觉使用起来挺麻烦的,而且还需要创建表格记录token,ticket. 总觉得 使用起来太耗性能了, 最近刚好看到了jwt【json web token】,于是百度了解了一下,觉得挺不错的.就在github上面搜索jwt 发现了lcobucci/jwt,然后就是composer,看了一下源码,以及简单的使用【readme.txt】,然后整理了一下。JWT适合一次性的命令认证,颁发一个有效期极短的JWT,即使暴露了危险也很小,由于每次操作都会生成新的JWT,实现无状态<?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,记录到这里。
最佳答案
评论() 相关
后面还有条评论,
评论支持使用[code][/code]标签添加代码
您需要登录后才可以评论 登录 | 立即注册
收藏
保护海
积分:2515 等级:LV3
热点推荐
(追記) (追記ここまで)
最新更新

我们

合作

网站

信息

ThinkPHP 是一个免费开源的,快速、简单的面向对象的 轻量级PHP开发框架 ,创立于2006年初,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而诞生的。ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,也注重易用性。并且拥有众多的原创功能和特性,在社区团队的积极参与下,在易用性、扩展性和性能方面不断优化和改进,已经成长为国内最领先和最具影响力的WEB应用开发框架,众多的典型案例确保可以稳定用于商业以及门户级的开发。

AltStyle によって変換されたページ (->オリジナル) /