ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
http://www.thinkphp.cn/topic/67206.html 这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,实现无状态**。` 1. <?php 3. namespace app\common\library; 5. useLcobucci\JWT\Builder; 6. useLcobucci\JWT\Signer\Key; 7. useLcobucci\JWT\ValidationData; 8. useLcobucci\JWT\Parser; 9. use think\Exception; 12. class JWT 13. { 14. /** 15.      * 现在的4.x是发行版 测试版 16.      * 使用3.3.1 稳定版本  17.      * composer require lcobucci/jwt 3.3.1 18.      */ 19. /** 20.      * 使用的加密类名称 21.      *  alg: 22.      * 使用公钥私钥 加密对 请使用下面的类 23.      * ['Lcobucci\JWT\Signer\Rsa\Sha256','Lcobucci\JWT\Signer\Rsa\Sha384','Lcobucci\JWT\Signer\Rsa\Sha512'] 24.      *  25.      * 使用字符串密钥类【单向不可逆加密算法】 请使用下面的类 26.      * ['Lcobucci\JWT\Signer\Hmac\Sha256','Lcobucci\JWT\Signer\Hmac\Sha384','Lcobucci\JWT\Signer\Hmac\Sha512'] 27.      *  28.      * Ecdsa类 --- 在github上找了一个 BitcoinPHP/BitcoinECDSA.php[这个已经很完整了,可以单独使用]  所以不加进来 29.      */ 30. private $alg ='Lcobucci\JWT\Signer\Hmac\Sha256'; 32. /** 33.      * 用户 aud  34.      */ 35. private $audience; 37. /** 38.      * 身份id  jti 39.      */ 40. private $id; 42. /** 43.      * 发布时间 iat 44.      */ 45. private $issuedAt; 47. /** 48.      * 发行人 iss 49.      */ 50. private $issuer; 52. /** 53.      * 主题 sub 54.      */ 55. private $subject; 57. /** 58.      * 到期时间 exp  59.      */ 60. private $expiration; 62. /** 63.      * 在此之前不可用  nbf 64.      */ 65. private $notBefore; 67. /** 68.      * 其他私有参数设置 比如uid  69.      */ 70. private $claims =[]; 72. /** 73.      * 加密私钥 用于加密 使用[file://]+[文件路径] 比如根目录下的prikey.txt文件 eg:file://prikey.txt 74.      */ 75. private $privateKey =null; 77. /** 78.      * 解密公钥 如果 不使用 非对称加密类进行签名 那么公钥值等于私钥值 79.      * 使用[file://]+[文件路径] 比如根目录下的pubkey.txt文件 eg:file://pubkey.txt 80.      */ 81. private $publicKey =null; 84. publicfunction __construct($config =[]) 85. { 86. if(!is_array($config)){ 87. thrownewException('构造参数必须是数组'); 88. } 89.         $time = time(); 90.         $this->alg = isset($config['alg'])&&!empty($config['alg'])? $config['alg']: $this->alg; 91.         $this->audience = isset($config['aud'])&&!empty($config['aud'])? $config['aud']: get_ip(); 92.         $this->id = isset($config['jti'])&&!empty($config['jti'])? $config['jti']: $this->getNoncestr(20); 93.         $this->issuedAt = isset($config['iat'])&&!empty($config['iat'])? $config['iat']: $time; 94.         $this->issuer = isset($config['iss'])&&!empty($config['iss'])? $config['iss']: $_SERVER['SERVER_NAME']; 95.         $this->subject = isset($config['sub'])&&!empty($config['sub'])? $config['sub']:'无主题'; 96.         $this->expiration = isset($config['exp'])&&!empty($config['exp'])? $time + $config['exp']: $time +3600; 97.         $this->notBefore = isset($config['nbf'])&&!empty($config['nbf'])? $config['nbf']: $time; 98.         $this->privateKey = isset($config['privateKey'])&&!empty($config['privateKey'])? $config['privateKey']:null; 99.         $this->publicKey = isset($config['publicKey'])&&!empty($config['publicKey'])? $config['publicKey']:null; 100.         $this->claims = isset($config['claims'])&& is_array($config['claims'])? $config['claims']: $this->claims; 101. } 103. /** 104.      * 使用私钥加密token 105.      */ 106. publicfunction getToken() 107. { 108.         $token =(newBuilder()) 109. ->issuedBy($this->issuer) 110. ->permittedFor($this->audience) 111. ->identifiedBy($this->id,true) 112. ->issuedAt($this->issuedAt) 113. ->relatedTo($this->subject) 114. ->canOnlyBeUsedAfter($this->notBefore) 115. ->expiresAt($this->expiration); 117. if(count($this->claims)>0){ 119. for($i =0; $i < count($this->claims);++$i){ 120.                 $token->withClaim($this->claims[$i][0], $this->claims[$i][1]); 121. } 122. } 124. if(is_null($this->privateKey)){ 125. return $token->getToken(); 126. } 128.         $signer =new $this->alg; 129.         $privateKey =newKey($this->privateKey); 131. return $token->getToken($signer,  $privateKey); 132. } 134. 1. /\*\* 2.      \* 验证 token 的有效性 3.      \*/ 4. publicfunction verify($token) 5. { 6. if(is\_null($this\->publicKey)){ 7. return\['result'\=>false,'errorMsg'\=>'解密密钥不能为空'\]; 8. } 9.         $signer \=new $this\->alg; 10.         $signer\_key \= $this\->publicKey; 11.         $token \=(newParser())->parse((string) $token); 12.         $data \=newValidationData(); 13. // $data->setIssuer($token->getClaim('iss')); 14. // $data->setAudience($token->getClaim('aud')); 15. // $data->setId($token->getHeader('jti')); 16. // $data->setSubject($token->getClaim('sub')); 17.         $data\->setIssuer($this\->issuer); 18.         $data\->setAudience($this\->audience); 19.         $data\->setId($this\->id); 20.         $data\->setSubject($this\->subject); 21. //如果没有使用加密,那么就只验证数据 22. if($token\->getHeader('alg')!=='none'){ 23. //验证密钥是否匹配【公钥私钥】 24. if(!$token\->verify($signer, $signer\_key)){ 25. return\['result'\=>false,'errorMsg'\=>'密钥不对'\]; 26. } 27. } 28. //验证token是否有效 数据比对不成功 29. if(!$token\->validate($data)){ 30. return\['result'\=>false,'errorMsg'\=>'签名错误'\]; 31. } 32. return\['result'\=>true,'msg'\=>'验证通过'\]; 33. } 174. /** 175.      * 生成非对称密钥对 176.      * 目的:方便自己生成密钥对,可以用于测试 177.      */ 178. publicfunction createRsaKey($alg ="sha256", $byte =1024, $type = OPENSSL_KEYTYPE_RSA) 179. { 180.         $config = array( 181. "digest_alg"=> $alg, 182. "private_key_bits"=> $byte,//512 1024 2048 4096 183. "private_key_type"=> $type, 184. ); 186. // 创建公钥和私钥 密钥对 187.         $res = openssl_pkey_new($config); 189. //从新建的密钥对立面获取私钥 190.         openssl_pkey_export($res, $privKey); 192. //从新建的密钥对立面获取公钥 193.         $pubKey = openssl_pkey_get_details($res); 194.         $pubKey = $pubKey["key"]; 196. return['result'=>true,'msg'=>'非对称密钥对生成成功','publicKey'=> $pubKey,'privateKey'=> $privKey]; 197. } 199. /** 200.      * 生成随机的字符作为本次请求的jti 识别id【标识】 201.      * @param $length int 202.      * 生成随机字符串 203.      * 大于10位,将(当前时间戳+7200  ---- 作为有效时间)隔个字符插入 204.      */ 205. privatefunction getNoncestr($length =20) 206. { 207. if($length >10){ 208.             $strs ="QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm"; 209.             $str = substr(str_shuffle($strs), mt_rand(0, strlen($strs)- $length -1-10), $length -10); 210.             $strArr = str_split($str,1); 211.             $timeArr = str_split(time()+3600,1); 212.             $string =""; 213. if(count($strArr)< count($timeArr)){ 215. for($i =0; $i < count($timeArr);++$i){ 217. if(isset($strArr[$i])){ 218.                         $string .= $strArr[$i]. $timeArr[$i]; 219. }else{ 220.                         $string .= $timeArr[$i]; 221. } 222. } 223. }else{ 225. for($i =0; $i < count($strArr);++$i){ 227. if(isset($timeArr[$i])){ 228.                         $string .= $strArr[$i]. $timeArr[$i]; 229. }else{ 230.                         $string .= $strArr[$i]; 231. } 232. } 233. } 234. }else{ 235.             $strs ="QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm"; 236.             $string = substr(str_shuffle($strs), mt_rand(0, strlen($strs)- $length -1), $length); 237. } 239. return $string; 240. } 242. /** 243.      * 设置属性 244.      */ 245. publicfunction __set($name, $val) 246. { 247. return $this->$name = $val; 248. } 250. /** 251.      * 获取属性 252.      */ 253. publicfunction __get($name) 254. { 255. return $this->$name; 256. } 257. } 复制代码 `使用方式:` 1. //一般作为签名:那么就是分两步,一是生成令牌token,然后在前端使用,可以放到header或者作为参数[eg:$_GET]  二获取并验证token 3. //方式一 使用私钥 获取token  Rsa 类   6.         echo '<pre>'; 8.         $conf =[ 9. 'privateKey'=>'file://private.txt', 10. 'alg'=>'Lcobucci\JWT\Signer\Rsa\Sha256', 11. 'exp'=>30, 12. 'sub'=>'ahai' 13. ]; 15.         $list =new JWT($conf); 17. /** 18.          * echo $token; 19.          * example: 20.          * eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjgxczV2Nzc3aDJnNTM5SzBONUM5In0.eyJpc3MiOiJ0ZXN0LmNtcy5uZXQiLCJhdWQiOiIxMC4wLjAuMTU1IiwianRpIjoiODFzNXY3NzdoMmc1MzlLME41QzkiLCJpYXQiOjE1NzcyNTU0NTksInN1YiI6ImFoYWkiLCJuYmYiOjE1NzcyNTU0NTksImV4cCI6MTU3NzI1OTA1OX0.dO0SiS6-85WSnz4SiBvb-BFR3oEVarwq6gWUvlZpMoId6w8WQ3Wx6WLlOSC8MWyn1ziapnhWP9usRfS5_3XicvzKI8fIilbFgHAhAJekxGXIOYw9TB66ggTuEQH4otLJH81hBwmfM4bJkd_N67kvh1HnpMMK3rdXIaiVWP9rKog 21.          */ 22.         $token = $list->getToken(); 23.         echo $token; 26. //方式一 使用公钥 解密token Rsa 类   27.         echo '<pre>'; 28.         $conf =[ 29. 'publicKey'=>'file://public.txt', 30. 'alg'=>'Lcobucci\JWT\Signer\Rsa\Sha256' 31. ]; 33.         $lists =new JWT($conf); 35.         var_dump($lists->verify($token)); 39. //方式二 使用相同的密钥作为公私钥 Hmac 类   41.         $conf =[ 42. 'privateKey'=>'公钥与私钥一样', 43. 'alg'=>'Lcobucci\JWT\Signer\Hmac\Sha256', 44. 'exp'=>3600 45. ]; 47.         $list =new JWT($conf); 49.         $token = $list->getToken(); 50.         echo $token; 52. //方式二 使用相同的密钥作为公私钥 Hmac 类   53.         $conf =[ 54. 'publicKey'=>'公钥与私钥一样', 55. 'alg'=>'Lcobucci\JWT\Signer\Hmac\Sha256' 56. ]; 58.         $lists =new JWT($conf); 59.         echo '<pre>'; 60.         var_dump($lists->verify($token)); 63. //token是有headers + payload + signature 中间使用"."做为连接符号   64. //验证token 65.         var_dump($lists->verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IngxeTU2N3M3STJUNWI5QzdTNU40In0.eyJpc3MiOiJ0ZXN0LmNtcy5uZXQiLCJhdWQiOiIxMC4wLjAuMTU1IiwianRpIjoieDF5NTY3czdJMlQ1YjlDN1M1TjQiLCJpYXQiOjE1NzcyNTYxNTQsInN1YiI6Ilx1NjVlMFx1NGUzYlx1OTg5OCIsIm5iZiI6MTU3NzI1NjE1NCwiZXhwIjoxNTc3MjU2MjE0fQ.-EhLYg0KlSyEMUdNJTAhk4-kcYvKr7G1_fwBWcuuEfs")); 复制代码 `ok,记录到这里。