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. <?php
  2. /**
  3. *   微信操作表
  4. *   wxtoken 表結構
  5. *   id
  6. *   access_token
  7. *   addtime
  8. *   wxticket 表結構
  9. *   id
  10. *   ticket
  11. *   addtime
  12. */
  13. class WX {
  14.     private $appid;
  15.     private $appserect;
  16.     private $curl;
  17.     private $msg;
  18.     protected $errs = array(
  19.         '-1' => '系統繁忙,此時請開發者稍候再試',
  20.         '0' => '請求成功',
  21.         '40001' => 'AppSecret錯誤或者AppSecret不屬於這個公衆號,請開發者確認AppSecret的正確性',
  22.         '40002' => '請確保grant_type字段值爲client_credential',
  23.         '40164' => '調用接口的IP地址不在白名單中,請在接口IP白名單中進行設置。',
  24.     );
  25.     function __construct($appid, $appserect) {
  26.         $this->appid = $appid;
  27.         $this->appserect = $appserect;
  28.         $this->curl = new Curl();
  29.     }
  30.     /*
  31.     微信網頁授權登錄  需要在公衆號設置 - 功能設置 - 網頁授權域名
  32.     第一步:用戶同意授權,獲取code
  33.     scope : snsapi_base 只能獲取openid 直接跳轉
  34.     snsapi_userinfo
  35.     */
  36.     public function getCode($redirect_uri, $scope = 'snsapi_userinfo',$state = '1') {
  37.         $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";
  38.         header("Location:{$url}");
  39.         exit;
  40.     }
  41.     /*
  42.     第二步:通過code換取網頁授權access_token
  43.     */
  44.     public function getAccessTokenByCode($code) {
  45.         $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appid}&secret={$this->appserect}&code={$code}&grant_type=authorization_code";
  46.         // exit($url);
  47.         // $curl = new Curl();
  48.         $result = $this->curl->doGet($url);
  49.         if (!$result) {
  50.             // $this->curl->getError()
  51.             $this->msg = "獲取token失敗";
  52.             return false;
  53.         }
  54.         $result = json_decode($result, true);
  55.         if ($result['errcode']) {
  56.             $this->msg = $result['errmsg'];
  57.             return false;
  58.         }
  59.         return $result;
  60.     }
  61.     // 第三步:刷新access_token(如果需要) 通過code 獲取openid $type 0靜默授權 1彈窗授權
  62.     public function getUserInfo($code, $type = 0, $lang = 'zh_CN ') {
  63.         $result = $this->getAccessTokenByCode($code);
  64.             if (!$result) {
  65.             return false;
  66.         }
  67.         $member = C::t(PT_USER)->getByOpenid($result['openid']);
  68.     if ($member) {
  69.         return $member;
  70.     } else {
  71.         if ($type) {
  72.             $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$result['access_token']}&openid={$result['openid']}&lang={$lang}";
  73.             // $return = $this->curl->doGet($url);
  74.             // 這接口有病 強制顯示文件頭
  75.             $return = file_get_contents($url);
  76.             if (!$return) {
  77.                 $this->msg = '獲取用戶信息失敗';
  78.                 return false;
  79.             }
  80.             $return = json_decode($return, true);
  81.             if (!$return) {
  82.                 $this->msg = '獲取用戶信息返回失敗';
  83.                 return false;
  84.             }
  85.             // file_put_contents('ccc.txt',print_r($return,true),FILE_APPEND);
  86.             $data = array(
  87.                 'openid' => $return['openid'],
  88.                 'name' => $return['nickname'],
  89.                 'sex' => $return['sex'],
  90.                 'province' => $return['province'],
  91.                 'city' => $return['city'],
  92.                 'country' => $return['country'],
  93.                 'img' => $return['headimgurl'],
  94.                 'bindtel' => 0,
  95.             );
  96.         } else {
  97.             $data = array(
  98.                 'openid' => $result['openid'],
  99.                 'username' => "微信用戶_" . random(6,1)
  100.             );
  101.         }
  102.         $name = rand(100000, 1000000000);
  103.         $e = $name . "@qq.com";
  104.         $password = $e;
  105.         $id = UserAddEdit(0, $data['username'], $password, $e,10,0,"", $msg);
  106.         if ($id <= 0) {
  107.             $this->msg = $msg;
  108.             return false;
  109.         }
  110.         C::t(PT_USER)->update($data, $id);
  111.         $member = C::t(PT_USER)->get($id);
  112.         return $member;
  113.         }
  114.     }
  115.     /*
  116.     公衆號 安全中心 設置IP白名單
  117.     公衆號的全局唯一接口調用憑據,公衆號調用各接口時都需使用access_token。開發者需要進行妥善保存。access_token的存儲至少要保留512個字符空間。access_token的有效期目前爲2個小時,需定時刷新,重複獲取將導致上次獲取的access_token失效。
  118.     */
  119.     public function getAccessToken($type) {
  120.         $addtime = TIMESTAMP - 7200;
  121.         $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->appserect}";
  122.         $row = C::t(PT_WXTOKEN)->getNew($addtime, $type);
  123.         if ($row) {
  124.             return $row['access_token'];
  125.         } else {
  126.             $result = $this->curl->doGet($url);
  127.             if (!$result) {
  128.                 $this->msg = "無法獲取令牌內容";
  129.                 return false;
  130.             }
  131.             $result = json_decode($result, true);
  132.             if (!$result) {
  133.                 $this->msg = "解析令牌內容失敗";
  134.                 return false;
  135.             }
  136.             if ($result['access_token']) {
  137.                 C::t(PT_WXTOKEN)->addToken($result['access_token'], $type);
  138.                 return $result['access_token'];
  139.             } else {
  140.                 $this->msg = "獲取令牌失敗";
  141.                 return false;
  142.             }
  143.         }
  144.     }
  145.     // 獲取js票據  需要在公衆號設置 - 功能設置 - JS接口安全域名設置
  146.     public function getJsTicket() {
  147.         $addtime = TIMESTAMP - 7200;
  148.         $row = C::t(PT_WXTICKET)->getNew($addtime);
  149.         if ($row) {
  150.             return $row['ticket'];
  151.         } else {
  152.             $token = $this->getAccessToken();
  153.             if (!$token) {
  154.                 return false;
  155.             }
  156.             $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$token}&type=jsapi";
  157.             $result = $this->curl->doGet($url);
  158.             if (!$result) {
  159.                 $this->msg = "無法獲取js票據";
  160.                 return false;
  161.             }
  162.             $result = json_decode($result, true);
  163.             if (!$result) {
  164.                 $this->msg = "解析js票據內容失敗";
  165.                 return false;
  166.             }
  167.             if ($result['ticket']) {
  168.                 C::t(PT_WXTICKET)->addTicket($result['ticket']);
  169.                 return $result['ticket'];
  170.             } else {
  171.                 $this->msg = "獲取js票據失敗";
  172.                 return false;
  173.             }
  174.         }
  175.     }
  176.     // js sdk 票據簽名 當前網頁的URL,不包含#及其後面部分
  177.     public function jsSign($data) {
  178.         // 1.所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)
  179.         ksort($data);
  180.         // 2.URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1 採用原始值,不進行URL 轉義
  181.         $string1 = $this->ToUrlParams($data);
  182.         // echo "string1:{$string1}<br/>";
  183.         // 3.對string1做sha1加密
  184.         $sign = sha1($string1);
  185.         // echo "signature:{$sign}<br/>";
  186.         return $sign;
  187.     }
  188.     // 獲取消息內容
  189.     public function getMsg() {
  190.         return $this->msg;
  191.     }
  192.     /**
  193.     * 格式化參數格式化成url參數
  194.     */
  195.     public function ToUrlParams($data) {
  196.         $buff = "";
  197.         foreach ($data as $k => $v) {
  198.             if ($k != "sign" && $v != "" && !is_array($v)) {
  199.                 $buff .= $k . "=" . $v . "&";
  200.             }
  201.         }
  202.         $buff = trim($buff, "&");
  203.         return $buff;
  204.     }
  205. }
  206. ?>

業務代碼:


 
  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. }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章