thinkphp5获取微信小程序的token和对token的验证

控制器:

class Token
{
    public function getToken($code='')
    {
        //返回的是json的数组,不是返回的字符串
        (new TokenGet())->goCheck();
        $ut = new UserToken($code);
        $token = $ut->get();
        return [
            'token'=>$token
        ];
    }

    //第三方获取令牌
    public function getAppToken($ac='',$se='')
    {
        (new AppTokenGet())->goCheck();
        $app = new AppToken();
        $token = $app->get($ac,$se);
        return [
            'token'=>$token
        ];
    }

    //验证token是否过期
    public function verifyToken($token='')
    {
        if (!$token){
            throw new ParameterException([
                'Token不能为空'
            ]);
        }
        $valid = TokenService::verifyToken($token);
        return [
            'isValid'=>$valid
        ];

    }
    
}

UserToken:

/**
 * 微信登录
 * 如果担心频繁被恶意调用,请限制ip
 * 以及访问频率
 */
class UserToken extends Token
{
    protected $code;
    protected $wxLoginUrl;
    protected $wxAppID;
    protected $wxAppSecret;

    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);
    }


    /**
     * 登陆
     * 思路1:每次调用登录接口都去微信刷新一次session_key,生成新的Token,不删除久的Token
     * 思路2:检查Token有没有过期,没有过期则直接返回当前Token
     * 思路3:重新去微信刷新session_key并删除当前Token,返回新的Token
     */
    public function get()
    {
        $result = curl_get($this->wxLoginUrl);

        // 注意json_decode的第一个参数true
        // 这将使字符串被转化为数组而非对象

        $wxResult = json_decode($result, true);
        if (empty($wxResult)) {
            // 为什么以empty判断是否错误,这是根据微信返回
            // 规则摸索出来的
            // 这种情况通常是由于传入不合法的code
            throw new Exception('获取session_key及openID时异常,微信内部错误');
        }
        else {
            // 建议用明确的变量来表示是否成功
            // 微信服务器并不会将错误标记为400,无论成功还是失败都标记成200
            // 这样非常不好判断,只能使用errcode是否存在来判断
            $loginFail = array_key_exists('errcode', $wxResult);
            if ($loginFail) {
                $this->processLoginError($wxResult);
            }
            else {
                return $this->grantToken($wxResult);
            }
        }
    }

    // 判断是否重复获取
    private function duplicateFetch(){
        //TODO:目前无法简单的判断是否重复获取,还是需要去微信服务器去openid
        //TODO: 这有可能导致失效行为
    }

    // 处理微信登陆异常
    // 那些异常应该返回客户端,那些异常不应该返回客户端
    // 需要认真思考
    private function processLoginError($wxResult)
    {
        throw new WeChatException(
            [
                'msg' => $wxResult['msg'],
                'errorCode' => $wxResult['errorCode']
            ]);
    }

    // 写入缓存
    private function saveToCache($wxResult)
    {
        $key = self::generateToken();
        $value = json_encode($wxResult);
        $expire_in = config('setting.token_expire_in');
        $result = cache($key, $value, $expire_in);

        if (!$result){
            throw new TokenException([
                'msg' => '服务器缓存异常',
                'errorCode' => 10005
            ]);
        }
        return $key;
    }

    // 颁发令牌
    // 只要调用登陆就颁发新令牌
    // 但旧的令牌依然可以使用
    // 所以通常令牌的有效时间比较短
    // 目前微信的express_in时间是7200秒
    // 在不设置刷新令牌(refresh_token)的情况下
    // 只能延迟自有token的过期时间超过7200秒(目前还无法确定,在express_in时间到期后
    // 还能否进行微信支付
    // 没有刷新令牌会有一个问题,就是用户的操作有可能会被突然中断
    private function grantToken($wxResult)
    {
        // 此处生成令牌使用的是TP5自带的令牌
        // 如果想要更加安全可以考虑自己生成更复杂的令牌
        // 比如使用JWT并加入盐,如果不加入盐有一定的几率伪造令牌
        //        $token = Request::instance()->token('token', 'md5');
        $openid = $wxResult['openid'];
        $user = User::getByOpenID($openid);
        if (!$user)
            // 借助微信的openid作为用户标识
            // 但在系统中的相关查询还是使用自己的uid
        {
            $uid = $this->newUser($openid);
        }
        else {
            $uid = $user->id;
        }
        $cachedValue = $this->prepareCachedValue($wxResult, $uid);
        $token = $this->saveToCache($cachedValue);
        return $token;
    }
    
    private function prepareCachedValue($wxResult, $uid)
    {
        $cachedValue = $wxResult;
        $cachedValue['uid'] = $uid;
        $cachedValue['scope'] = ScopeEnum::User;
//        $cachedValue['scope'] = 15;
        return $cachedValue;
    }

    // 创建新用户
    private function newUser($openid)
    {
        // 有可能会有异常,如果没有特别处理
        // 这里不需要try——catch
        // 全局异常处理会记录日志
        // 并且这样的异常属于服务器异常
        // 也不应该定义BaseException返回到客户端
        $user = User::create(
            [
                'openid' => $openid
            ]);
        return $user->id;
    }
}

Token:

 // 生成令牌
    public static function generateToken()
    {
        $randChar = getRandChar(32);
        $timestamp = $_SERVER['REQUEST_TIME_FLOAT'];
        $tokenSalt = config('secure.token_salt');
        return md5($randChar . $timestamp . $tokenSalt);
    }
    //验证token是否合法或者是否过期
    //验证器验证只是token验证的一种方式
    //另外一种方式是使用行为拦截token,根本不让非法token
    //进入控制器
    public static function needPrimaryScope()
    {
        $scope = self::getCurrentTokenVar('scope');
        if ($scope) {
            if ($scope >= ScopeEnum::User) {
                return true;
            }
            else{
                throw new ForbiddenException();
            }
        } else {
            throw new TokenException();
        }
    }

    // 用户专有权限
    public static function needExclusiveScope()
    {
        $scope = self::getCurrentTokenVar('scope');
        if ($scope){
            if ($scope == ScopeEnum::User) {
                return true;
            } else {
                throw new ForbiddenException();
            }
        } else {
            throw new TokenException();
        }
    }

    public static function needSuperScope()
    {
        $scope = self::getCurrentTokenVar('scope');
        if ($scope){
            if ($scope == ScopeEnum::Super) {
                return true;
            } else {
                throw new ForbiddenException();
            }
        } else {
            throw new TokenException();
        }
    }

    public static function getCurrentTokenVar($key)
    {
        $token = Request::instance()
            ->header('token');
        $vars = Cache::get($token);
        if (!$vars)
        {
            throw new TokenException();
        }
        else {
            if(!is_array($vars))
            {
                $vars = json_decode($vars, true);
            }
            if (array_key_exists($key, $vars)) {
                return $vars[$key];
            }
            else{
                throw new Exception('尝试获取的Token变量并不存在');
            }
        }
    }

    /**
     * 从缓存中获取当前用户指定身份标识
     * @param array $keys
     * @return array result
     * @throws \app\lib\exception\TokenException
     */
    public static function getCurrentIdentity($keys)
    {
        $token = Request::instance()
            ->header('token');
        $identities = Cache::get($token);
        //cache 助手函数有bug
//        $identities = cache($token);
        if (!$identities)
        {
            throw new TokenException();
        }
        else
        {
            $identities = json_decode($identities, true);
            $result = [];
            foreach ($keys as $key)
            {
                if (array_key_exists($key, $identities))
                {
                    $result[$key] = $identities[$key];
                }
            }
            return $result;
        }
    }

    /**
     * 当需要获取全局UID时,应当调用此方法
     *而不应当自己解析UID
     *
     */
    public static function getCurrentUid()
    {
        $uid = self::getCurrentTokenVar('uid');
        $scope = self::getCurrentTokenVar('scope');
        if ($scope == ScopeEnum::Super)
        {
            // 只有Super权限才可以自己传入uid
            // 且必须在get参数中,post不接受任何uid字段
            $userID = input('get.uid');
            if (!$userID)
            {
                throw new ParameterException(
                    [
                        'msg' => '没有指定需要操作的用户对象'
                    ]);
            }
            return $userID;
        }
        else
        {
            return $uid;
        }
    }

    /**
     * 检查操作UID是否合法
     * @param $checkedUID
     * @return bool
     * @throws Exception
     * @throws ParameterException
     */
    public static function isValidOperate($checkedUID)
    {
        if(!$checkedUID){
            throw new Exception('检查UID时必须传入一个被检查的UID');
        }
        $currentOperateUID = self::getCurrentUid();
        if($currentOperateUID == $checkedUID){
            return true;
        }
        return false;
    }

    public static function verifyToken($token)
    {
        $exist = Cache::get($token);
        if($exist){
            return true;
        }
        else{
            return false;
        }
    }
}

extra下的secure:

//为了随机生成
return [
    'token_salt'=>'HH12TieBs377mJtKr',
    'pay_back_url'=>'http://www.zerg.com/v1/pay/notify'
];

微信小程序:
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 写微信小程序后端需要以下几个步骤: 1. 环境搭建:需要搭建 PHP 环境,安装 ThinkPHP 框架,以及安装微信开发者工具。 2. 注册小程序:在微信公众平台注册小程序获取小程序的 AppID 和 AppSecret,用于后续开发中的身份验证和 API 调用。 3. 配置接口:在微信公众平台中配置小程序接口信息,包括接口地址、Token、EncodingAESKey 等信息。 4. 开发 API:根据小程序需求,设计并开发相应的 API 接口,可以使用 ThinkPHP 提供的 RESTful API 快速搭建。 5. 身份验证:在开发 API 的过程中,需要对请求进行身份验证,确保只有授权的用户可以访问相应的 API。 6. 数据库操作:使用 ThinkPHP 提供的数据库操作接口,对数据库进行增、删、改、查等操作。 7. 返回数据:在 API 接口的实现中,需要将查询到的数据返回给小程序端。可以使用 JSON 格式或其他格式。 8. 调试和部署:在开发过程中,需要进行调试和测试。当测试通过后,可以将代码部署到服务器上,供小程序调用。 总的来说,使用 ThinkPHP 开发微信小程序后端,需要掌握 PHP 开发技术和数据库操作技术,并熟悉微信小程序开发的流程和接口调用方式。 ### 回答2: thinkphp是一款基于PHP语言开发的MVC框架,用于构建Web应用程序。如果我们想要使用thinkphp来开发微信小程序后端,我们可以按照以下思路进行: 1. 搭建开发环境:首先,我们需要在本机搭建PHP开发环境,并安装thinkphp框架。可以使用XAMPP、WAMP或者LAMP等工具来搭建环境,并从thinkphp官方网站下载最新版本的框架。 2. 创建项目和模块:在thinkphp框架中,我们可以使用命令行工具或者手动创建来创建一个新的项目。然后,我们可以根据微信小程序的需求,创建相应的控制器、模型和视图等模块。 3. 配置数据库连接:在thinkphp的配置文件中,我们需要配置数据库连接信息,以便于在后端与数据库进行交互。可以在配置文件中设置数据库的类型、主机地址、数据库名、用户名和密码等。 4. 数据库操作:利用thinkphp提供的ORM(对象关系映射)功能,我们可以通过定义模型类来简化与数据库的交互。可以使用模型中提供的方法来进行数据的增删改查操作,以及数据的关联查询等。 5. 接口开发:对于微信小程序后端的交互,我们可以定义相应的接口来处理前端的请求。通过接口,前端可以发送HTTP请求到后端,并传递相应的参数。后端可以根据接口进行数据的处理和逻辑的操作,并返回相应的结果给前端。 6. 接口权限验证:在微信小程序中,我们可以使用微信提供的登录机制来获取用户的OpenID,并将其作为用户的唯一标识。在后端接口中,我们可以通过验证用户的OpenID来进行接口权限的控制,确保只有合法的用户才能访问接口。 7. 数据返回格式化:在后端接口开发过程中,我们需要将数据以统一的格式返回给前端。可以使用thinkphp提供的返回格式封装函数,将数据封装成JSON格式并返回给前端。 总之,使用thinkphp开发微信小程序后端需要通过搭建开发环境、创建项目和模块、配置数据库连接、数据库操作、接口开发、接口权限验证等步骤。这样,我们就可以基于thinkphp框架来实现微信小程序后端逻辑和数据交互。 ### 回答3: thinkphp是一款开源的PHP框架,适用于快速开发Web应用程序。如果要使用thinkphp来开发微信小程序后端,可以按照以下思路进行: 1. 配置环境:首先,确保服务器上已经安装了PHP环境,并下载、安装好thinkphp框架,同时要确保安装了与微信小程序交互的SDK。 2. 数据库设计:根据小程序的需求,设计好数据库的表结构,包括用户表、数据表等。 3. 创建控制器:根据业务逻辑,在thinkphp中创建相应的控制器文件,用于处理来自小程序前端的请求。 4. 实现接口:在控制器中实现与小程序前端交互的接口,包括用户登录、数据查询、数据添加、数据更新等功能。可以使用thinkphp提供的数据库查询方法、数据验证、数据过滤等函数。 5. 用户验证:在接口中实现用户登录验证的逻辑,可以通过小程序前端传递的用户标识进行验证,并同时保存用户信息到数据库中。 6. 数据交互:通过接口,实现小程序前端与后台的数据交互。可以使用thinkphp提供的数据返回函数,将查询到的数据以JSON格式返回给小程序前端。 7. 安全性考虑:在小程序后端开发过程中,需要考虑数据的安全性。可以使用thinkphp的安全函数对接收到的数据进行过滤,避免SQL注入等安全问题。 8. 错误处理:在接口中,加入错误处理机制,对可能出现的错误进行捕获和处理,并返回相应的错误信息给小程序前端。 9. 日志记录:使用thinkphp提供的日志记录功能,对后端的运行情况进行记录,以便后期的排查和调试。 总之,使用thinkphp开发微信小程序后端,需要根据小程序的需求进行数据库设计,创建相应的控制器文件,实现接口以及相应的业务逻辑,并加入相应的安全性考虑和错误处理机制,以确保小程序后端的稳定性和安全性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

骨子里的偏爱

上传不易,还请多多支持。

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

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

打赏作者

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

抵扣说明:

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

余额充值