PHP开发微信授权登录教程

微信的授权登录和QQ、新浪等平台的授权登录都大同小异,均采用OauthOAuth2.0鉴权方式。

 

微信授权分为两种:

1、静默授权

2、弹窗授权,需要用户手动同意


两种scope的区别说明

1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)

2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

具体而言,网页授权流程分为四步:

1、引导用户进入授权页面同意授权,获取code

2、通过code换取网页授权access_token(与基础支持中的access_token不同)

3、如果需要,开发者可以刷新网页授权access_token,避免过期

4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)

以下是封装的微信操作类,需要用到两个数据表,用于保存access_token、ticket,由于他们具有一定有效期,且每天请求数有上限,所以开发者需自行保存,以下是代码:

 
  1. /**
  2. *   微信操作表
  3. *   wxtoken 表结构
  4. *   id
  5. *   access_token
  6. *   addtime
  7. *   wxticket 表结构
  8. *   id
  9. *   ticket
  10. *   addtime
  11. */
  12. class WX {
  13.     private $appid;
  14.     private $appserect;
  15.     private $curl;
  16.     private $msg;
  17.     protected $errs = array(
  18.         '-1' => '系统繁忙,此时请开发者稍候再试',
  19.         '0' => '请求成功',
  20.         '40001' => 'AppSecret错误或者AppSecret不属于这个公众号,请开发者确认AppSecret的正确性',
  21.         '40002' => '请确保grant_type字段值为client_credential',
  22.         '40164' => '调用接口的IP地址不在白名单中,请在接口IP白名单中进行设置。',
  23.     );
  24.     function __construct($appid, $appserect) {
  25.         $this->appid = $appid;
  26.         $this->appserect = $appserect;
  27.         $this->curl = new Curl();
  28.     }
  29.     /*
  30.     微信网页授权登录  需要在公众号设置 - 功能设置 - 网页授权域名
  31.     第一步:用户同意授权,获取code
  32.     scope : snsapi_base 只能获取openid 直接跳转
  33.     snsapi_userinfo
  34.     */
  35.     public function getCode($redirect_uri, $scope = 'snsapi_userinfo',$state = '1') {
  36.         $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appid}&redirect_uri={$redirect_uri}&response_type=code&scope={$scope}&state={$state}#wechat_redirect";
  37.         header("Location:{$url}");
  38.         exit;
  39.     }
  40.     /*
  41.     第二步:通过code换取网页授权access_token
  42.     */
  43.     public function getAccessTokenByCode($code) {
  44.         $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appid}&secret={$this->appserect}&code={$code}&grant_type=authorization_code";
  45.         // exit($url);
  46.         // $curl = new Curl();
  47.         $result = $this->curl->doGet($url);
  48.         if (!$result) {
  49.             // $this->curl->getError()
  50.             $this->msg = "获取token失败";
  51.             return false;
  52.         }
  53.         $result = json_decode($result, true);
  54.         if ($result['errcode']) {
  55.             $this->msg = $result['errmsg'];
  56.             return false;
  57.         }
  58.         return $result;
  59.     }
  60.     // 第三步:刷新access_token(如果需要) 通过code 获取openid $type 0静默授权 1弹窗授权
  61.     public function getUserInfo($code, $type = 0, $lang = 'zh_CN ') {
  62.         $result = $this->getAccessTokenByCode($code);
  63.             if (!$result) {
  64.             return false;
  65.         }
  66.         $member = C::t(PT_USER)->getByOpenid($result['openid']);
  67.     if ($member) {
  68.         return $member;
  69.     } else {
  70.         if ($type) {
  71.             $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$result['access_token']}&openid={$result['openid']}&lang={$lang}";
  72.             // $return = $this->curl->doGet($url);
  73.             // 这接口有病 强制显示文件头
  74.             $return = file_get_contents($url);
  75.             if (!$return) {
  76.                 $this->msg = '获取用户信息失败';
  77.                 return false;
  78.             }
  79.             $return = json_decode($return, true);
  80.             if (!$return) {
  81.                 $this->msg = '获取用户信息返回失败';
  82.                 return false;
  83.             }
  84.             // file_put_contents('ccc.txt',print_r($return,true),FILE_APPEND);
  85.             $data = array(
  86.                 'openid' => $return['openid'],
  87.                 'name' => $return['nickname'],
  88.                 'sex' => $return['sex'],
  89.                 'province' => $return['province'],
  90.                 'city' => $return['city'],
  91.                 'country' => $return['country'],
  92.                 'img' => $return['headimgurl'],
  93.                 'bindtel' => 0,
  94.             );
  95.         } else {
  96.             $data = array(
  97.                 'openid' => $result['openid'],
  98.                 'username' => "微信用户_" . random(6,1)
  99.             );
  100.         }
  101.         $name = rand(100000, 1000000000);
  102.         $e = $name . "@qq.com";
  103.         $password = $e;
  104.         $id = UserAddEdit(0, $data['username'], $password, $e,10,0,"", $msg);
  105.         if ($id <= 0) {
  106.             $this->msg = $msg;
  107.             return false;
  108.         }
  109.         C::t(PT_USER)->update($data, $id);
  110.         $member = C::t(PT_USER)->get($id);
  111.         return $member;
  112.         }
  113.     }
  114.     /*
  115.     公众号 安全中心 设置IP白名单
  116.     公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
  117.     */
  118.     public function getAccessToken($type) {
  119.         $addtime = TIMESTAMP - 7200;
  120.         $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->appserect}";
  121.         $row = C::t(PT_WXTOKEN)->getNew($addtime, $type);
  122.         if ($row) {
  123.             return $row['access_token'];
  124.         } else {
  125.             $result = $this->curl->doGet($url);
  126.             if (!$result) {
  127.                 $this->msg = "无法获取令牌内容";
  128.                 return false;
  129.             }
  130.             $result = json_decode($result, true);
  131.             if (!$result) {
  132.                 $this->msg = "解析令牌内容失败";
  133.                 return false;
  134.             }
  135.             if ($result['access_token']) {
  136.                 C::t(PT_WXTOKEN)->addToken($result['access_token'], $type);
  137.                 return $result['access_token'];
  138.             } else {
  139.                 $this->msg = "获取令牌失败";
  140.                 return false;
  141.             }
  142.         }
  143.     }
  144.     // 获取js票据  需要在公众号设置 - 功能设置 - JS接口安全域名设置
  145.     public function getJsTicket() {
  146.         $addtime = TIMESTAMP - 7200;
  147.         $row = C::t(PT_WXTICKET)->getNew($addtime);
  148.         if ($row) {
  149.             return $row['ticket'];
  150.         } else {
  151.             $token = $this->getAccessToken();
  152.             if (!$token) {
  153.                 return false;
  154.             }
  155.             $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$token}&type=jsapi";
  156.             $result = $this->curl->doGet($url);
  157.             if (!$result) {
  158.                 $this->msg = "无法获取js票据";
  159.                 return false;
  160.             }
  161.             $result = json_decode($result, true);
  162.             if (!$result) {
  163.                 $this->msg = "解析js票据内容失败";
  164.                 return false;
  165.             }
  166.             if ($result['ticket']) {
  167.                 C::t(PT_WXTICKET)->addTicket($result['ticket']);
  168.                 return $result['ticket'];
  169.             } else {
  170.                 $this->msg = "获取js票据失败";
  171.                 return false;
  172.             }
  173.         }
  174.     }
  175.     // js sdk 票据签名 当前网页的URL,不包含#及其后面部分
  176.     public function jsSign($data) {
  177.         // 1.所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)
  178.         ksort($data);
  179.         // 2.URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1 采用原始值,不进行URL 转义
  180.         $string1 = $this->ToUrlParams($data);
  181.         // echo "string1:{$string1}
    ";
  182.         // 3.对string1做sha1加密
  183.         $sign = sha1($string1);
  184.         // echo "signature:{$sign}
    ";
  185.         return $sign;
  186.     }
  187.     // 获取消息内容
  188.     public function getMsg() {
  189.         return $this->msg;
  190.     }
  191.     /**
  192.     * 格式化参数格式化成url参数
  193.     */
  194.     public function ToUrlParams($data) {
  195.         $buff = "";
  196.         foreach ($data as $k => $v) {
  197.             if ($k != "sign" && $v != "" && !is_array($v)) {
  198.                 $buff .= $k . "=" . $v . "&";
  199.             }
  200.         }
  201.         $buff = trim($buff, "&");
  202.         return $buff;
  203.     }
  204. }
  205. ?>

业务代码:

 
  1. // 微信登录
  2. function wxlogin() {
  3.     global $_G,$identifier,$config,$wx;
  4.     if (!$_G['uid']) {
  5.         if ($_GET['state']) {
  6.             //回调
  7.             $member = $wx->getUserInfo($_GET['code']);
  8.             if (!$member) {
  9.                 exit($wx->getMsg());
  10.             }
  11.             if (!function_exists("setloginstatus")) {
  12.                 include_once libfile('function/member');
  13.             }
  14.             // 设置登录状态$wx
  15.             setloginstatus($member, 2592000);
  16.             checkfollowfeed();
  17.             $_G['uid'] = $member['uid'];
  18.             $_G['member'] = $member;
  19.         } else {
  20.             //请求授权 对参数编码
  21.             $redirect = urlencode(getProtocol() . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
  22.             $wx->getCode($redirect, 'snsapi_base');
  23.         }
  24.     }
  25. }
  26. function getProtocol() {
  27.     return is_HTTPS() ? 'https://' : 'http://';
  28. }
  29. function is_HTTPS() {  if ($_SERVER['HTTPS'] === 1 || $_SERVER['HTTPS'] === 'on' || $_SERVER['SERVER_PORT'] == 443) {
  30.         return true;
  31.     }
  32.     return false;
  33. }

你可能感兴趣的:(微信h5授权登录)