PHP万能token类--纯原生

所有的项目中都会涉及到身份鉴权的使用,而目前最常用的是通过token(令牌)进行鉴权;

token的解析:(来源于:https://www.jianshu.com/p/24825a2683e6)

1、Token的引入:Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。

2、Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

3、使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。


为了更好在各项目中适用,下面将贴出一个token类,此类不仅支持校验,还支持多用户的登录校验,以便一个用户在多个客户端的鉴权,不多说,代码如下:

<?php
namespace app\api\controller;
/**
 * 拿来即用的token类
 * user Yang
 * QQ   952323608
 * wechat  ygy952323608
 * tip:此类与传统的token有点不一样,创建token时会返回一个值,一个是tokenname 一个是onlykey 检查是否有效时,需要两个值都进行传入
 */
class Token extends Common {
    private $redis;
    private $maxNum = 3;//可同时在线人数
    private $continueLife = false;//在检查token有效后,是否自动延长token的有效期
    private $resetKey = true;//在检查有效后,是否自动将其移动到队列末端
    private $config = array(
        'tokenTiming'=>3600*24*14,//token有效期
        'secret'=>'Hkasnu765./HBViasp;.12',//秘钥请勿泄露
        'tokenPrefix'=>'tokenOnlyYang',//token前缀,保证redis的key值不被覆盖   此项请勿随意修改,否则会影响 destructToken 方法的有效性
    );
    private $randLength = 35;//获取token时,onlykey的长度
    //public $errTip = false;//挤退是否进行提醒---待开发  被挤退后对 onlykey 进行一定时间内的缓存,保存被挤退的状态
    /**
     * 可闯入config设置项来区别不同类型的 token 避免token重名
     */
    public function __construct($config=[])
    {
        $this->redis = getRedis();
        if ($this->maxNum<=0 || !is_numeric($this->maxNum)){
            $this->maxNum = 1;
        }
        if ($this->maxNum>=20){
            $this->maxNum = 20;
        }
        if (!empty($config)){
            isset($config['maxNum']) && $this->maxNum = $config['maxNum'];
            isset($config['continueLife']) && $this->continueLife = $config['continueLife'];
            isset($config['resetKey']) && $this->resetKey = $config['resetKey'];
            isset($config['config']) && $this->config = $config['config'];
            isset($config['randLength']) && $this->randLength = $config['randLength'];
        }

    }

    /**
     * 获取当前redis连接柄
     */
    public function getRedisConnect(){
        return $this->redis;
    }

    /**
     * 获取指定唯一值的token值
     */
    private function tokenName($onlyKey){
        return ($this->config['tokenPrefix']).$onlyKey.'_token';
    }
    /**
     * 创建token
     * @param  string $onlykey 创建的唯一值,保证每个用户对应的唯一性 如uid
     */
    public function createToken($onlyKey){
        $tokenName = $this->tokenName($onlyKey);
        $randStr = md5(getRandomStr($this->randLength,false).$this->config['secret']);//随机字符,用于挤兑下线
        $this->checkTokenLife($tokenName);
        $token_info = $this->redis->get($tokenName);
        $token_list = empty($token_info)?[]:json_decode($token_info,true);
        $num = count($token_list);
        $token_list[$num]['lifetime'] = time()+$this->config['tokenTiming'];
        $token_list[$num]['randstr'] = $randStr;
        $this->redis->set($tokenName,json_encode($token_list),$this->config['tokenTiming']);
        $this->checkMaxOnline($tokenName);
        $this->checkMaxLife($tokenName);
        return array(
            'token'=>$tokenName,
            'onlykey'=>$randStr
        );
    }

    /**
     * 处理token的过期数据
     * @param string $tokenName 令牌名称
     */
    private function checkTokenLife($tokenName){
        $token_strlist = array();
        $list_str = $this->redis->get($tokenName);
        !empty($list_str) && $token_strlist = json_decode($list_str,true);
        foreach ($token_strlist as $tk=>$tv){
            if ($tv['lifetime']<time()){
                unset($token_strlist[$tk]);
            }
        }
        $token_strlist = array_values($token_strlist);
        if (empty($token_strlist)){
            $this->redis->del($tokenName);
        }else{
            $this->redis->set($tokenName,json_encode($token_strlist));
            $this->checkMaxLife($tokenName);
        }
        return true;
    }

    /**
     * 检查同时在线人数限制
     * @param string $tokenName 检查的tokenname
     */
    private function checkMaxOnline($tokenName){
        $token_result = $this->redis->get($tokenName);

        $token_list = empty($token_result)?[]:json_decode($token_result,true);
        while (count($token_list)>$this->maxNum){
            //大于最多允许在线人数
            array_shift($token_list);
        }
        $new_list = array_values($token_list);
        $this->redis->set($tokenName,json_encode($new_list));
        return true;
    }

    /**
     * 检查token及onlykey是否合法
     * @param string $token
     * @param string $onlykey createToken处返回的onlykey
     * @param string $getInfo 是否同时返回数据
     * @return bool/array
     */
    public function checkTokenStatus($tokenName,$randStr,$getInfo=false){
        $this->checkMaxOnline($tokenName);
        $this->checkTokenLife($tokenName);
        $token_rs = $this->redis->get($tokenName);
        if (empty($token_rs)){
            return false;
        }
        $token_list = empty($token_rs)?[]:json_decode($token_rs,true);
        foreach ($token_list as $tk=>$tv){
            if ($tv['randstr'] == $randStr){
                $this->continueLife === true && $token_list[$tk]['lifetime'] = time()+$this->config['tokenTiming'];//自动延期
                if ($this->resetKey === true){
                    //重置key
                    $token_list[count($token_list)] = $token_list[$tk];
                    unset($token_list[$tk]);
                    $token_list = array_values($token_list);
                }
                $this->redis->set($tokenName,json_encode($token_list));
                $this->checkMaxLife($tokenName);
                if ($getInfo === false){
                    return true;
                }else{
                    $info = !isset($tv['info'])||empty($tv['info'])?[]:json_decode($tv['info'],true);
                    return $info;
                }
            }
        }
        return false;
    }

    /**
     * 设置数据
     * @param string $tokenName token名
     * @param string $onlyKey 唯一标识
     * @param string $key 设置key
     * @param string/array $value 设置value
     * @param bool $allset 是否所有token 列表的所有者都设置
     * @return bool
     */
    public function setTokenInfo($tokenName,$onlyKey,$key,$value,$allset=true){
        if ($key == 'lifetime' || $key == 'randstr'){
            return false;//配置项不可修改
        }
        $old_continueLife = $this->continueLife;
        $old_resetKey = $this->resetKey;
        $this->continueLife = false;
        $this->resetKey = false;
        $status = $this->checkTokenStatus($tokenName,$onlyKey,false);
        $this->continueLife = $old_continueLife;
        $this->resetKey = $old_resetKey;
        if ($status === true){
            $token_list = json_decode($this->redis->get($tokenName),true);
            foreach ($token_list as $tk=>$tv){
                if ($allset == true){
                    $token_list[$tk][$key] = $value;
                }else{
                    if ($tv['randstr'] == $onlyKey){
                        $token_list[$tk][$key] = $value;
                        break;
                    }
                }
            }
            $this->redis->set($tokenName,json_encode($token_list));
            $this->checkMaxLife($tokenName);
            return true;
        }else{
            return false;
        }
    }

    /**
     * 删除数据
     * @param string $tokenName token名
     * @param string $onlyKey 唯一标识
     * @param string $key 设置key
     * @param bool $allset 是否所有token 列表的所有者都删除
     * @return bool
     */
    public function removeTokenInfo($tokenName,$onlyKey,$key,$allset=true){
        if ($key == 'lifetime' || $key == 'randstr'){
            return false;//配置项不可修改
        }
        $old_continueLife = $this->continueLife;
        $old_resetKey = $this->resetKey;
        $this->continueLife = false;
        $this->resetKey = false;
        $status = $this->checkTokenStatus($tokenName,$onlyKey,false);
        $this->continueLife = $old_continueLife;
        $this->resetKey = $old_resetKey;
        if ($status === true){
            $token_list = json_decode($this->redis->get($tokenName),true);
            foreach ($token_list as $tk=>$tv){
                if ($allset == true){
                    unset($token_list[$tk][$key]);
                }else{
                    if ($tv['randstr'] == $onlyKey){
                        unset($token_list[$tk][$key]);
                        break;
                    }
                }
            }
            $this->redis->set($tokenName,json_encode($token_list));
            $this->checkMaxLife($tokenName);
            return true;
        }else{
            return false;
        }
    }

    /**
     * 获取数据
     * @param string $tokenName token名
     * @param string $onlyKey 唯一标识
     * @param string $key 获取key
     * @return string
     */
    public function getTokenInfo($tokenName,$onlyKey,$key){
        $old_continueLife = $this->continueLife;
        $old_resetKey = $this->resetKey;
        $this->continueLife = false;
        $this->resetKey = false;
        $status = $this->checkTokenStatus($tokenName,$onlyKey,false);
        $this->continueLife = $old_continueLife;
        $this->resetKey = $old_resetKey;
        if ($status === true){
            $token_list = json_decode($this->redis->get($tokenName),true);
            foreach ($token_list as $tk=>$tv){
                if ($tv['randstr'] == $onlyKey){
                    return isset($tv[$key])?$tv[$key]:'';
                }
            }
            $this->redis->set($tokenName,json_encode($token_list));
            $this->checkMaxLife($tokenName);
            return true;
        }else{
            return false;
        }
    }

    /**
     * 检查token的最长周期
     */
    private function checkMaxLife($tokenName){
        $token_rs = $this->redis->get($tokenName);
        if (empty($token_rs)){
            return true;
        }
        $token_list = json_decode($token_rs,true);
        $max_lifetime = 0;
        foreach ($token_list as $tk=>$tv){
            if ($tv['lifetime']>$max_lifetime){
                $max_lifetime = $tv['lifetime']-time();
            }
        }
        if ($max_lifetime<=0){
            $this->redis->del($tokenName);
        }else{
            $this->redis->set($tokenName,json_encode($token_list),$max_lifetime);
        }
        return true;
    }

    /**
     * 设置配置项
     */
    public function setConfig($key,$value){
        $this->$key = $value;
    }

    /**
     * 销毁token  修改密码时使用
     */
    public function destructToken($tokenName){
        $this->redis->del($tokenName);
    }
}

//使用方法
$uid = 1000;
$obj = new Token();
$tokenArr = $obj->createToken($uid);//新建token 必须保证uid是唯一的,如用户id
$obj->checkTokenStatus($tokenArr['token'],$tokenArr['onlykey'],true);//检查token是否有效,无效为false 有效则返回true或token保存的信息  

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南风拂衣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值