视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
php如何设置权限令牌token
2020-11-02 18:27:20 责编:小采
文档


php设置token的方法:1、定义获取Token的路由路径;2、建立Service层;3、使用UserToken类处理整个逻辑;4、在Model层里建立User类;5、在验证器类和异常类创建相应的验证方法和异常处理。

推荐:《PHP视频教程》

PHP_设置权限令牌Token

我们开发的后端API接口会对访问者有一个权限要求,比如一些包含私人信息的接口,就需要访问者请求接口的同时,传递一个提前已经发放给访问者的Token。

这就像一个令牌一样,只有访问者展示出来我们才会“通过放行”。

下面就记录一下权限令牌的代码编写思路。


一、流程概要

  • 定义获取Token的路由路径,接受code参数(code来源:微信服务器,登录系统基于微信体系)

  • 建立Service层,在这层里创建Token基类和UserToken类

  • UserToken类处理整个逻辑:Token生成和返回

  • 在Model层里建立User类,负责用户数据表的读写,供Service层的UserToken调用

  • 在验证器类和异常类创建相应的验证方法和异常处理

  • 控制器->Service层->Model层返回值给Service层->Service层返回值给控制器,整个流程完成Token令牌的编写

  • 二、具体说明

    首先定义好路由路径:

    Route::post(
     'api/:version/token/user',
     'api/:version.Token/getToken'
    );

    然后创建Token控制器,定义对应路由路径的getToken方法:

    public function getToken($code='') {
     (new TokenGet())->goCheck($code); // 验证器
     $token = (new UserToken($code))->get();
     return [
     'token' => $token
     ];
     }

    在调用Service层之前,还得检查一下传递过来的参数,于是定义TokenGet这个验证器:

    class TokenGet extends BaseValidate
    {
     protected $rule = [
     'code' => 'require|isNotEmpty'
     ];
    
     protected $message = [
     'code' => '需要code才能获得Token!'
     ];
     }

    回到Token控制器,验证通过后,我们调用Service层定义的UserToken类:

    $token = (new UserToken($code))->get();复制代码

    这里讨论一下Service层和Model层。我们普遍的理解是Service层是基于Model层的一次抽象封装。

  • Model层只负责操作数据库并返且返回给Service层
  • 然后Service层处理业务逻辑,最后返回给Controller层
  • 但我觉得小项目的话,Service其实和Model就有点平级的意思,因为有些简单的接口Model层直接对接Controller就可以了,只有相对复杂的接口,比如用户权限,就可以再经过Service层分隔不同功能的代码。

    这样的处理更加灵活,有大量确实很简单的接口就不用过一次Service层了,这样更像是走过过场而已,没什么意义了。

    回到Service层的代码编写,由于Token还会有不同的种类,所以先创建一个Token基类,里面包含一些通用的方法。然后就是给访问者返回令牌的UserToken类的编写了。

    由于是基于微信,我们需要三个信息:code,appid,appsecret,然后通过构造函数来给UserToken类赋上初始值:

    function __construct($code) {
     $this->code = $code;
     $this->wxAppID = config('wx.app_id');
     $this->wxAppSecret = config('wx.app_secret');
     $this->wxLoginUrl = sprintf(
     config('wx.login_url'),
     $this->wxAppID, $this->wxAppSecret, $this->code
     );
     }

    然后把这三个放入微信提供的接口的参数位置,目的是获得一个完整的微信服务器端的url,请求到我们需要的openid。

    然后是通过发送网络请求的步骤就在此略过。微信服务器会返回包含openid的对象,判断这个对象的值没问题后,我们就开始生成令牌的步骤了,创建函数grantToken():

    private function grantToken($openidObj) {
    
     // 取出openid
     $openid = $openidObj['openid'];
     
     // 通过Model层调用数据库,检查openid是否已经存在
     $user = UserModel::getByOpenID($openid);
     
     // 如果存在,不处理,反之则新增一条user记录
     if ($user) {
     $uid = $user->id;
     } else {
     // 不存在,生成一条数据,具体方法略过
     $uid = $this->newUser($openid); 
     }
     
     // 生成令牌,写入缓存(具体方法见下面的定义)
     $cachedValue = $this->prepareCacheValue($openidObj, $uid);
     $token = $this->saveToCache($cachedValue);
     
     // 令牌返回到调用者端
     return $token;
    }
    
    private function prepareCacheValue($openidObj, $uid) {
     $cachedValue = $openidObj;
     $cachedValue['uid'] = $uid;
     $cachedValue['scope'] = 16; // 权限值,自己定义
     return $cachedValue;
    }
     
    private function saveToCache($cachedValue) {
     $key = self::generateToken(); // 生成令牌的方法
     $value = json_encode($cachedValue);
     $tokenExpire = config('setting.token_expire'); // 设定的过期时间
    
     $request = cache($key, $value, $tokenExpire);
     if (!$request) {
     throw new TokenException([
     'msg' => '服务器缓存异常',
     'errorCode' => 10005
     ]);
     }
     return $key; // 返回令牌:token
    }

    可以看到,核心流程就是:

  • 拿到openid
  • 查看数据库,检查openid是否已经存在
  • 如果存在,不处理,反之则新增一条user记录
  • 生成令牌,准备缓存数据,写入缓存
  • 把令牌返回到客户端去
  • 其中generateToken()这个方法详细定义如下:

    public static function generateToken() {
     $randomChars = getRandomChars(32); // 32个字符组成一组随机字符串
     $timestamp = $_SERVER['REQUEST_TIME_FLOAT']; 
     $salt = config('security.token_salt'); // salt 盐
     // 拼接三组字符串,进行MD5加密,然后返回
     return md5($randomChars.$timestamp.$salt);
    }
     
    function getRandomChars($length) {
     $str = null;
     $strPoll = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567';
     $max = strlen($strPoll) - 1;
    
     for ($i = 0; $i < $length; $i++) {
     $str .= $strPoll[rand(0, $max)];
     }
     return $str;
    }

    它的主要作用毫无疑问就是生成我们的需要的令牌——Token字符串。值得一提的是由于generateToken()在其他类型的Token里也会用到,所以是放在Token基类里的。

    至此,只需要把生成的令牌再返回到Controller就行了。

    三、总结

    令牌的编写涉及到很多的流程,为了避免混乱,一定要注意把负责不同工作的代码分别定义在不同的方法里。就像上面例子里grantToken()方法体现的那样,这是个核心方法,包含所有流程,但是不同的具体流程又定义在其他方法里,然后提供给grantToken()方法调用。

    这样做之后grantToken()方法即使包含所有流程,但也依然很容易阅读。

    下载本文
    显示全文
    专题