美文网首页
TP5 + 微信小程序实现微信登陆

TP5 + 微信小程序实现微信登陆

作者: 铁匠简记 | 来源:发表于2018-06-14 17:00 被阅读205次

1、微信开发中openid用于唯一标记用户身份,但是openid关系用户隐私,没有过期时间,一旦丢失后果严重;
2、小程序官方文档已经给出了微信登录获取openid的相关接口,但是由于出于安全考虑,不能直接将openid返回给客户端;
3、思路简述:小程序登录获取code,传入code调用服务端程序UserToken获取openid、session_key,可以存在服务端,比如缓存中;为用户生成token用于后续请求的身份验证。

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 processLoginError($wxResult)
    {
        throw new WeChatException(
            [
                'msg' => $wxResult['errmsg'],
                'errorCode' => $wxResult['errcode']
            ]);
    }
    // 写入缓存
    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;
        return $cachedValue;
    }

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

相关文章

网友评论

      本文标题:TP5 + 微信小程序实现微信登陆

      本文链接:https://www.haomeiwen.com/subject/dibyeftx.html