2 次代碼提交 2e055b813f ... 494a623733

作者 SHA1 備註 提交日期
  panda 494a623733 Merge branch 'master' of http://git.huxiukeji.com/lizhen/xiaoyou 6 月之前
  panda 7d820f769a 修改config下Redis配置and增加了几个工具类 6 月之前

+ 1 - 0
.gitignore

@@ -1,4 +1,5 @@
 .idea
+.env
 runtime
 *.htaccess
 uploads

+ 7 - 3
application/config.php

@@ -335,11 +335,15 @@ return [
 
     ],
 
-    'redis'                  => [
+    'redis' => [
         // 主机
-        'host'     => '127.0.0.1',
+        'host' => Env::get('redis.REDIS_HOST', '127.0.0.1'),
+        // 密码
+        'password' => Env::get('redis.REDIS_PASSWORD', ''),
         // 端口
-        'port'      => 6379,
+        'port' => Env::get('redis.REDIS_PORT', 6379),
+        'select' => Env::get('redis.REDIS_PORT', 6379),
+        'prefix' => Env::get('app.app_name', 'thinkphp'),
     ],
 
     // 运营商一键登录      校友缘 正式

+ 83 - 0
application/utils/CheckSignUtil.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace app\utils;
+
+use think\Request;
+
+/**
+ * 校验sign 校验签名,防止刷重要接口
+ */
+class CheckSignUtil{
+
+    /**
+     * 校验接口名单
+     */
+    const hitList = [
+        'api/gift/givegift_typing',// 聊天赠送礼物
+        'api/usercenter/chat_once',// 文字聊天扣费
+        'api/usercenter/voice_onemin',// 语音聊天扣费
+        'api/usercenter/videochat_onemin',// 视频聊天扣费
+    ];
+
+    /**
+     * 失效时间 单位 seconds
+     */
+    const deadTime = 150;
+
+    /**
+     * 校验签名
+     * @param $path
+     * @param $token
+     * @param $sign
+     * @return array
+     */
+    public static function check($path,$token,$sign): array
+    {
+        if (!in_array($path,self::hitList)){
+            return [true,'success'];
+        }
+
+        if (empty($sign)){
+            return [false,'签名缺失!'];
+        }
+
+        // 签名解密
+        $rsa = new RsaUtil();
+        $sign = $rsa->privateDecrypt($sign);
+        if (!$sign || !$sign = json_decode($sign,true)){
+            return [false,'签名错误!'];
+        }
+
+        if (empty($sign['token']) || empty($sign['timestamp']) || empty($sign['timezone'])){
+            return [false,'签名参数错误!'];
+        }
+
+        if ($token != $sign['token']){
+            return [false,'签名参数校验错误!'];
+        }
+
+        if ($token != $sign['token']){
+            return [false,'签名参数校验错误!'];
+        }
+
+        // 根据客户端 时区 校验时间戳
+        $now = new \DateTime(null, new \DateTimeZone((string)$sign['timezone']));
+        $time = (int)($now->format('U'));
+
+        // 如果 前端请求时间戳异常,则认为 请求被篡改 或 请求超时
+        if ($sign['timestamp'] <= ($time - self::deadTime)){
+            return [false,'签名过期,请求已过期!'];
+        }
+
+        // 请求唯一key
+        $key = md5($sign['token'] . '_' .((string)$sign['timestamp']));
+
+        // redis 重复请求
+        if (!RedisUtil::getInstance('check_sign_lock',$key)->tryTimes(self::deadTime,1)) {
+            return [false,'点的太快啦!'];
+        }
+
+        return [true,'success'];
+    }
+
+}

+ 17 - 0
application/utils/RedisKeyEnum.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace app\utils;
+
+class RedisKeyEnum
+{
+    /**
+     * @Message("用户信息")
+     */
+    const ORDER_NO                 = 'ORDER_NO:';                // 订单编号
+    const API_REQUEST_TRAFFIC      = 'API_REQUEST_TRAFFIC:';     // 接口流量
+    const WX_MINI_APP_ACCESS_TOKEN = 'WX_MINI_APP_ACCESS_TOKEN:';// 微信 小程序 access_token
+    const TOKEN_ONCE               = 'TOKEN_ONCE:';              // 设置唯一token
+    const TOKEN_TIME               = 'TOKEN_TIME:';              // 设置token时间
+    const ROOM_USER_IN             = 'ROOM_USER_IN:';            // 用户当前所在直播间
+    const ROOM_USER_LIST           = 'ROOM_USER_LIST:';          // 直播间观众列表
+}

+ 326 - 0
application/utils/RedisUtil.php

@@ -0,0 +1,326 @@
+<?php
+
+namespace app\utils;
+
+class RedisUtil
+{
+    private        $Key;
+    protected      $Redis;
+    private static $instances = [];
+    protected      $options   = [
+        'host'       => '127.0.0.1',
+        'port'       => 6379,
+        'password'   => '',
+        'select'     => 0,
+        'timeout'    => 0,//
+        'expire'     => 0,
+        'persistent' => false,
+        'prefix'     => '',
+    ];
+
+    public function __construct(string $key)
+    {
+        // 判断是否有扩展(若是你的apache没reids扩展就会抛出这个异常)
+        if (!extension_loaded('redis')) {
+            throw new \BadFunctionCallException('not support: redis');
+        }
+        $this->options = config('redis');
+        $func          = $this->options['persistent'] ? 'pconnect' : 'connect';// 判断是否长链接
+        $this->Redis   = new \Redis;
+        $redis         = $this->Redis->$func($this->options['host'], $this->options['port'], $this->options['timeout']);
+        if (!$redis) {
+            throw new \BadFunctionCallException('connection fail: redis');
+        }
+        if ($this->options['password'] != '') {
+            $this->Redis->auth($this->options['password']);
+        }
+
+        if (0 != $this->options['select']) {
+            $this->Redis->select($this->options['select']);
+        }
+
+        $this->Key = $this->options['prefix'] . ':' . $key;
+    }
+
+    /**
+     * @param string $key
+     * @param string ...$dynamic key动态值拼接
+     * @return RedisUtil|mixed
+     * @throws \Exception
+     */
+    public static function getInstance(string $key, string ...$dynamic)
+    {
+        $dynamics = '';
+        foreach ($dynamic as $k => $v) {
+            if ($k == 0) {
+                $dynamics .= $v;
+            } else {
+                $dynamics .= '_' . $v;
+            }
+        }
+
+        $instanceName = uniqid();
+        if (!isset(self::$instances[$instanceName])) {
+            self::$instances[$instanceName] = new self($key . $dynamics);
+        }
+
+        return self::$instances[$instanceName];
+    }
+
+    /**
+     * 生成唯一标识
+     * @param int $length 生成长度
+     * @return string
+     */
+    public function createNo(int $length = 5): string
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        //指定键值新增+1 并获取
+        $reqNo = $redis->incr($key);
+
+        //值长度已达到5位,且首位已为9,初始化
+        if (strlen($reqNo) >= $length && substr($reqNo, 0, 1) == 9) {
+            $redis->set($key, 1);
+            $reqNo = 1;
+        }
+
+        if ($reqNo == 1) {
+            //设置月末到期
+            $expire = strtotime(date('Y-m', strtotime("+1 month")) . '-01 00:00:00') - time();
+            $redis->expire($key, $expire);
+        }
+
+        return sprintf("%0{$length}d", $reqNo);
+    }
+
+    /**
+     * 限制尝试次数 (设定时间内)
+     * @param int $second 秒
+     * @param int $Times 次数
+     * @return false|int
+     */
+    public function tryTimes(int $second, int $Times = 5)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        //指定键值新增+1 并获取
+        $count = $redis->incr($key);
+        if ($count > $Times) {
+            return false;
+        }
+
+        //设置过期时间
+        if ($count == 1) {
+            $redis->expire($key, $second);
+        }
+
+        return (int)$count;
+    }
+
+    /**
+     * 加锁
+     * @param int $timeout 锁的过期时间
+     * @return false|string
+     */
+    public function Lock(int $timeout = 10)
+    {
+        $redis    = $this->Redis;
+        $lockName = $this->Key;//锁的名字
+
+        #获取唯一标识符
+        $identifier = uniqid();
+
+        #锁的过期时间
+        $end = time() + $timeout;
+
+        #循环获取锁
+        while (time() < $end) {
+            #查看$lockName是否被上锁,为$lockName设置过期时间,防止死锁
+            if ($redis->set($lockName, $identifier, ['ex' => $timeout, 'nx'])) {
+                return $identifier;
+            }
+
+            #停止0.001ms
+            usleep(0.001);
+        }
+        return false;
+    }
+
+    /**
+     * 释放锁
+     * @param string $identifier 锁的唯一值
+     * @return bool
+     */
+    public function releaseLock(string $identifier): bool
+    {
+        $redis    = $this->Redis;
+        $lockName = $this->Key;//锁的名字
+
+        // 判断是锁有没有被其他客户端修改
+        if ($redis->get($lockName) != $identifier) {
+            #其他客户端修改了锁,不能删除别人的锁
+            return false;
+        }
+
+        #释放锁
+        $redis->del($lockName);
+        return true;
+    }
+
+    public function setex($value, int $second)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->setex($key, $second, $value);
+    }
+
+    public function get()
+    {
+        $redis = $this->Redis;
+        $key   = $this->Key;
+
+        return $redis->get($key);
+    }
+
+    public function del()
+    {
+        $redis = $this->Redis;
+        $key   = $this->Key;
+
+        return $redis->del($key);
+    }
+
+    //加入list
+    public function lPush($value)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->lPush($key, $value);
+    }
+
+    //删除右边
+    public function rPop()
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->rPop($key);
+    }
+
+    public function rPush($value)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->rPush($key, $value);
+    }
+
+    public function lPopLine($num)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        $pipe = $redis->multi(\Redis::PIPELINE);
+        $pipe->lRange($key, 0, $num - 1);
+        $pipe->lTrim($key, $num, -1);
+        return $pipe->exec();
+    }
+
+
+    //list长度
+    public function lLen()
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->llen($key);
+    }
+
+    public function zadd($score, $member)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->zadd($key, $score, $member);
+    }
+
+    public function zcard()
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->zcard($key);
+    }
+
+    public function zrem($member)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->zrem($key, $member);
+    }
+
+    // 递增返回
+    public function zRange($start_score = 0, $end_score = -1)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->zrange($key, $start_score, $end_score);
+    }
+
+    // 递减返回
+    public function zRevRange($start_score = 0, $end_score = -1)
+    {
+        $redis = $this->Redis;
+
+        $key = $this->Key;
+
+        return $redis->zRevRange($key, $start_score, $end_score);
+    }
+
+    public function hSet($field, $value)
+    {
+        $redis = $this->Redis;
+        $key = $this->Key;
+        return $redis->hset($key, $field, $value);
+    }
+
+    public function hKeys()
+    {
+        $redis = $this->Redis;
+        $key = $this->Key;
+        return $redis->hkeys($key);
+    }
+
+    public function hGet($field)
+    {
+        $redis = $this->Redis;
+        $key = $this->Key;
+        return $redis->hget($key, $field);
+    }
+
+    public function hDel($field)
+    {
+        $redis = $this->Redis;
+        $key = $this->Key;
+        return $redis->hdel($key, $field);
+    }
+}

+ 169 - 0
application/utils/RsaUtil.php

@@ -0,0 +1,169 @@
+<?php
+
+namespace app\utils;
+
+/**
+ * RSA 加密工具类
+ */
+class RsaUtil{
+    const RSA_ALGORITHM_KEY_TYPE = OPENSSL_KEYTYPE_RSA;
+    const RSA_ALGORITHM_SIGN = OPENSSL_ALGO_SHA256;
+
+    protected $public_key; // 公钥
+    protected $private_key; // 私钥
+    protected $key_len;
+
+    /**
+     * @throws \Exception
+     */
+    public function __construct($pub_key = '', $pri_key = '')
+    {
+        $pub_key = file_get_contents(!empty($pub_key) ? $pub_key : APP_PATH.'/utils/certs/public_key.pem'); // 获取公钥文件中的数据
+        $pri_key = file_get_contents(!empty($pri_key) ? $pri_key : APP_PATH.'/utils/certs/private_key.pem'); // 获取私钥文件中的数据
+
+        if ($pub_key) {
+            $this->public_key = $pub_key;
+            $pub_id = openssl_pkey_get_public($this->public_key);
+            $this->key_len = openssl_pkey_get_details($pub_id)['bits'];
+        }
+
+        if ($pri_key) {
+            $this->private_key = $pri_key;
+            $pri_id = openssl_pkey_get_private($this->private_key);
+            $this->key_len = openssl_pkey_get_details($pri_id)['bits'];
+        }
+    }
+
+    /**
+     * 创建密钥对
+     *
+     * @param $key_size
+     * @return array
+     */
+    public static function createKeys($key_size = 1024)
+    {
+        $config = array(
+            "private_key_bits" => $key_size,
+            "private_key_type" => self::RSA_ALGORITHM_KEY_TYPE,
+        );
+        $res = openssl_pkey_new($config);
+        openssl_pkey_export($res, $private_key);
+        $public_key_detail = openssl_pkey_get_details($res);
+        $public_key = $public_key_detail["key"];
+
+        return array(
+            "public_key" => $public_key,
+            "private_key" => $private_key,
+        );
+    }
+
+    /**
+     * 私钥解密
+     *
+     * @param $encrypted
+     * @return string
+     */
+    public function privateDecrypt($encrypted)
+    {
+        $decrypted = "";
+        $part_len = $this->key_len / 8;
+        //url  中的get传值默认会吧+号过滤成' ',替换回来就好了
+        str_replace('% ', '+', $encrypted);
+        $base64_decoded = base64_decode($encrypted);
+        $parts = str_split($base64_decoded, $part_len);
+        foreach ($parts as $part) {
+            $decrypted_temp = '';
+            openssl_private_decrypt($part, $decrypted_temp, $this->private_key);
+            $decrypted .= $decrypted_temp;
+        }
+        return $decrypted;
+    }
+
+    /**
+     * 私钥加密
+     *
+     * @param $data
+     * @return string
+     */
+    public function privateEncrypt($data)
+    {
+        $encrypted = '';
+        $part_len = $this->key_len / 8 - 11;
+        $parts = str_split($data, $part_len);
+
+        foreach ($parts as $part) {
+            $encrypted_temp = '';
+            openssl_private_encrypt($part, $encrypted_temp, $this->private_key);
+            $encrypted .= $encrypted_temp;
+        }
+        return base64_encode($encrypted);
+    }
+
+    /**
+     * 公钥解密
+     *
+     * @param $encrypted
+     * @return string
+     */
+    public function publicDecrypt($encrypted)
+    {
+        $decrypted = "";
+        $part_len = $this->key_len / 8;
+        $base64_decoded = base64_decode($encrypted);
+        $parts = str_split($base64_decoded, $part_len);
+
+        foreach ($parts as $part) {
+            $decrypted_temp = '';
+            openssl_public_decrypt($part, $decrypted_temp, $this->public_key);
+            $decrypted .= $decrypted_temp;
+        }
+        return $decrypted;
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param $data
+     * @return string
+     */
+    public function publicEncrypt($data)
+    {
+        $encrypted = '';
+        $part_len = $this->key_len / 8 - 11;
+        $parts = str_split($data, $part_len);
+
+        foreach ($parts as $part) {
+            $encrypted_temp = '';
+            openssl_public_encrypt($part, $encrypted_temp, $this->public_key);
+            $encrypted .= $encrypted_temp;
+        }
+
+        return base64_encode($encrypted);
+    }
+
+    /**
+     * 数据加签
+     *
+     * @param $data
+     * @return string
+     */
+    public function sign($data)
+    {
+        openssl_sign($data, $sign, $this->private_key, self::RSA_ALGORITHM_SIGN);
+        return base64_encode($sign);
+    }
+
+    /**
+     * 数据签名验证
+     *
+     * @param $data
+     * @param $sign
+     * @return false|int
+     */
+    public function verify($data, $sign)
+    {
+        $pub_id = openssl_get_publickey($this->public_key);
+        $res = openssl_verify($data, base64_decode($sign), $pub_id, self::RSA_ALGORITHM_SIGN);
+        return $res;
+    }
+}

+ 28 - 0
application/utils/certs/private_key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4Ft+gCIy3weZM
+cL+d6PratqUp2BqsTZScjFOXGXf7VqspilWnV9w5VY03Yn30QSGJiMX7YsAqmd7X
+wMDtsWjEdy33afyH1mv3HmzUjC76tYJZRDwi3iL/UX4f5wg3q/SnEdt0eUjsvyth
+5Uv3dSPu33VamlKupHi7cNLnyGiVtx6XCUWPZduI/dx8HIhPv+ljBMXEhfd2nBNS
+DgzMxNjfw4o7bFHCFqIEjOrkkuhZEbzkpUqu+nXT0lyjUNN7PZKRTFXmyiFJp9w8
+oSYTUxRyVDQxXFmaFFNz/T9dlX5zZJt3eOlxrlMawzfJT8sjTPUh/SaWGAfbz6O5
+BvotsINdAgMBAAECggEAGRKUdplzgPuZwEOC9aDruaoyqJZUW69dVE0QfX3sh4Dw
+YwICVH508iIiz65MdV4BkVfdjzWN8r9uSHhqTnVhTZDl49K9oEql/cxZCkHljCP4
+kuvmPOGAZsHUqcevqNV0xuR5vU1fj08iO30dcaC0ui4ZL7PhGwQvpWficcsr42gI
+K5YpXrzIAHeqnh1aO0nFZ+72Dpv8tJ84vzjYHVL69jUE/+ZcPsUg4L3xrJqv7xgj
+cn5mV8Tp/Vq90oBjpOMwnwnqnP9uNJXm06z7fN5B1oupGO8oL8apUTtXiDz5ki4H
+EQkITBiWfyd2ZnrzB5m+g6i291d3zrk6Ktr1AsA1yQKBgQDpIw4GYnsr3QfP21R2
+HIWeKiRFbJz47k/P0qXc4gmtbNgVMvRcuMiFqxo9EWq0ArUscSrGQo9DHlHmNE4I
+mLt46I1cDQGbd94aAF6YbhGRRY9nALdOOcwn9Prwmu3ag4e0BIZXdUGi/+TBZotg
+YTi/UowuwpdXNQj/idSJ6EG35wKBgQDKJHhhR3hnIdi0/MnljiTg4l3kRxosAHhk
+Wj/EXHIVj3xfYb1qpn2sNptHP52Mcwot1Il24a9Za22sB7LT6UC5LBYPKaC8xTkc
+wy1pSILiFWFKkCSGVI5ZCx8yCOgtKuX9Gc5zyDN7g0Qs6Jul0t0GrVV5dZdQgn6S
+K7kf+ZEyGwKBgAsLpyF0TwOgADu/13uVFFuPAZLsd29v0wY0o7kYWH8H9iDi/iLa
+el2i4qZcCeMnca/+WJl2E9WJPTYb6kqL+e3bjr76usXD4xj2RYURSfHJCudFaGnj
+YSHPgIgfvv8oViiYQkkofAid+ReUpSvWz1p5Lb0VaRgvohKBVDmIcHynAoGABWUF
+xWrX3sC7fNEGPlcVyt6frNjs7OGyVNNHwmDJNqQWqKD+IhuBXRgz+qnK9ZWYXvK+
+HGsXd+ZzDdVCw38FGCwCBrOQs5j71jWrP5UM0SXK6+d+4grdpaizU22DbLkpXCDV
+LqfYH+K37g7jIIDJH0V+83fc6uwgnpwGG8FYS6MCgYEAsWZ06tZGVgfC5HqxM4n1
+lasT6m7ea/CVkeCOnZeK1JLxTTqKehTGhFlzXIO7vGv5QvyaIWRDzIlftRY3lxTR
+7AR++C8NIwSG5Z3BbYs0XAf+SlG+fvLcu4RAYy/FFrqhm4Jqpt4zQnqFOshpdPey
+oK2YLlr97PEbtGL0HCIEBcA=
+-----END PRIVATE KEY-----

+ 9 - 0
application/utils/certs/public_key.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuBbfoAiMt8HmTHC/nej6
+2ralKdgarE2UnIxTlxl3+1arKYpVp1fcOVWNN2J99EEhiYjF+2LAKpne18DA7bFo
+xHct92n8h9Zr9x5s1Iwu+rWCWUQ8It4i/1F+H+cIN6v0pxHbdHlI7L8rYeVL93Uj
+7t91WppSrqR4u3DS58holbcelwlFj2XbiP3cfByIT7/pYwTFxIX3dpwTUg4MzMTY
+38OKO2xRwhaiBIzq5JLoWRG85KVKrvp109Jco1DTez2SkUxV5sohSafcPKEmE1MU
+clQ0MVxZmhRTc/0/XZV+c2Sbd3jpca5TGsM3yU/LI0z1If0mlhgH28+juQb6LbCD
+XQIDAQAB
+-----END PUBLIC KEY-----