Oauth+thinkphp的理解与实践

Oauth+thinkphp的理解与实践

  1. 首先 , Oauth 是什么?

具体的可参见阮一峰的Oauth的讲解,地址,其中,阐述的大家可以看一看,我主要针对的是其中的 授权码模式。

  • 授权码模式:

      1. 用户打开客户端,客户端请求用户给与授权
      1. 用户同意授权
      1. 客户端获得授权后,向认证服务器(发放授权方)申请令牌
      1. 客户端得到令牌(可以设置权限)后,就可以向自己的资源服务器拿权限范围之内的资料了

举个例子-我要问妈妈拿钱买零食:

首先,我要产生买零食的欲望(步骤1 ),有买零食的欲望后(2),我要和妈妈说,要妈妈同意并且给我钱,我才能买,不然妈妈会打我并且不让我买零食(3) , 我拿到钱后,只要我有足够多的钱(权限)就可以去商店随便买我卖的钱(权限范围)的零食了

授权码模式 模式是其中最复杂也是最安全的模式

  1. 实践

只要用过微信小程序的人,便知道,当第一次使用的时候,它总是会叫你授权。
对的,这就是授权码模式中的 行为1

这里我主要针对微信的登录流程并结合授权码模式进行实践

技术支持:thinkphp

工具支持:tool - 下载地址:

在线文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

小程序登录行为处理:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html

接口:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html

登录接口使用:GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

拉取用户信息微信登录流程如下

  • 获取code,获取code

    注册小程序号测试号(建议新建一个邮箱,公众号账号和小程序账号不同), 创建一个测试
    打开设置->开发设置 , 保存你的AppID 和 AppSecret
    打开微信开发工具,导入项目文件,启动工具 (下载地址)[]
    点击申请令牌,获取code

  • 通过code换取网页授权access_token、openid

      /**
      * @return string token 辨认用户信息
      * @throws CacheException
      * @throws Exception
      * @throws WechatException
      */
      public function get()
      {
          // 发起一个 HTTP请求 通过code换取网页授权access_token 的行为
          // 类似于: https://api.weixin.qq.com/sns/jscode2session?appid=你的appid&secret=你的SECRET&js_code=JSCODE&grant_type=authorization_code
    
          $result = curl_get($this->wxLoginUrl); 
          $result = json_decode($result , true);
    
          //  如果为空的话,则是微信内部出现错误了
          if( empty($result) ) {
              throw new Exception('获取access_token 和 oppenid时 发生异常:error,内部错误');
          }else{
              // 错误情况下的返回: {"errcode":40029,"errmsg":"invalid code"}
              // 检查是否存在错误码
              if( array_key_exists('errcode' , $result) ) {
                  // 处理一个异常信息,可忽略
                  $this->processLoginError($result);
              }
          }
    
              // 获取一个token
              $token = $this->grantToken($result);
    
              return $token;
    
          }
    
    
          /**
          * 处理流程:
          * 通过openid检查数据库中是否存在这个用户,否则创建,返回当前授权用户id
          * 准备缓存数据,写入$result+$uid信息,过期时间查看system配置项
          * 写入缓存,返回存入缓存的key=token
          * @param $uid integer 用户的id
          * @param $result array 获得的微信授权后得到的信息
          * @return string token 数据(存放用户信息)
          * @throws CacheException 异常处理
          */
          public function grantToken($result)
          {
              $openid = $result['openid'];
              // 检查数据库中是否存在这个用户
              $user = (new UserModel)->getUserOpenid($openid);
    
              // 当数据库中不存在这个 用户的时候插入用户
              if( !$user ) {
                  $user = $this->newUser($openid);
              }
              $uid = $user->id;
    
              // 准备缓存数据
              $cachedValue = $this->prepareCacheData($result , $uid);
    
              // 写入缓存
              $token = $this->saveToAllValue($cachedValue);
    
              return $token;
          }
    
          /**
          * 写入缓存处理逻辑
          * @param $value array $result+$uid : 缓存的信息
          * @return string token
          * @throws CacheException
          */
          public function saveToAllValue($value) {
              $key = self::generateToken(32);
              $value = json_encode($value);
              $expire_in = config('system.expire_in');
              $result = cache($key , $value , $expire_in);
              if( !$result ) {
                  throw new CacheException([
                      'msg' => '服务器缓存出现异常',
                      'errCode' => '30000'
                  ]);
              }
              return $key;
          }
    

    处理流程,当我执行get方法时,发起了一个针对 https://api.weixin.qq.com/sns/jscode2session?appid=你的appid&secret=你的SECRET&js_code=JSCODE&grant_type=authorization_code 的get 请求。

    请求成功:

      { 
          "access_token":"ACCESS_TOKEN",
          "expires_in":7200,
          "refresh_token":"REFRESH_TOKEN",
          "openid":"OPENID",
          "scope":"SCOPE" 
      }
    

    请求失败:

      {"errcode":40029,"errmsg":"invalid code"}
    

    一般除了invalid code错误之外,还会出现code已使用的情况。

    记住:code 只能使用一次

  • 资源服务器发放token

    当取得授权后,说明你用户已经成功的加入了小程序。对用户信息进行存档

    疑问:如何避免用户的权限问题呢(不能对资源服务器随意的发起任何资源请求)
    每个人的信息是独一无二的,不能让其他人看到,或者需要登录查看

    这就需要涉及到发放资源服务器token了

    资源服务器针对每个用户当前的一段时间的行为给与一个token,客户端可以携带这个token向客户端证明自己的身份来请求相关资源


    设置token相关代码:

      $key = randomkeys($length);  // 随机一个字符串
      $salt = config('system.token_salt'); // 加盐
      $time = $_SERVER['REQUEST_TIME_FLOAT']; // 当前请求时间(防止token重复)
      return md5($key . $salt . $time);  // 生成token
    

    token 只是一个key值,通过携带的token,服务器可以token向缓存文件匹配,拿到属于这个用户的信息 可参见 saveToAllValue 方法

当然,我使用的这种方法还是比较浅显,可以参见 Jwt

原文: http://surest.cn/article/44

posted @ 2018-10-19 01:32  mysure  阅读(665)  评论(0)    收藏  举报