在公眾號的配置過程中,許多開發者會在菜單中加入HTML5頁面,有時在頁面內需要訪問頁面的用戶信息,此時就需要網頁授權獲取用戶基本信息
PS:本博文所闡述的微信開發基於Yii2.0框架
1、設置授權回調域名:開發 ---> 接口權限
找到“網頁授權獲取用戶基本信息”,點擊後面對應的“修改”,在彈框響應位置填寫授權回調域名即可,此處的域名不需要加http:// (關於網頁授權回調域名的說明詳情可參考公眾平台開發者文檔)
2、獲取授權
關於OAuth2.0博主參考的是方倍工作室的博文http://www.cnblogs.com/txw1958/p/weixin71-oauth20.html(PS:方倍是一個微信開發大神,其中的微信開發內容還是比較詳細的,推薦參考),其中詳細剖析了微信官方文檔的相關內容,也提供了獲取授權的更詳細思路和方案。
實際上,獲取用戶信息的關鍵在於獲取用戶的openid。博主想要實現用戶點擊公眾號菜單打開頁面即可自動授權,從而針對該用戶進行數據庫操作,於是有下面兩種方式:
(1)利用自定義菜單請求授權頁面
自定義菜單後面會單獨寫一篇博文,在這裡先簡述一下通過自定義菜單進行授權,該方法需要高級接口權限,且局限於關注公眾號的用戶直接從菜單進入頁面。
1 $menu = '{ 2 "button":[ 3 { 4 "type": "view", 5 "name": "商城", 6 "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx&redirect_uri=http://tx.heivr.com/index.php&response_type=code&scope=snsapi_base&state=1#wechat_redirect" 7 }, 8 9 { 10 "name":"快遞服務", 11 "sub_button":[ 12 { 13 "type":"click", 14 "name":"發快遞", 15 "key":"express" 16 }, 17 { 18 "type":"click", 19 "name":"快遞查詢", 20 "key":"ww" 21 } 22 ] 23 }, 24 ] 25 }';
需要授權的view直接在url處填寫微信提供的授權請求地址,其中:
1、以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的用戶的openid的,並且是靜默授權並自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業務頁面) 2、以snsapi_userinfo為scope發起的網頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,並且由於用戶同意過,所以無須關注,就可在授權後獲取該用戶的基本信息。
按照此方法點擊“商城”即可接收到返回的openid,繼而進行下一步用戶信息的獲取。
(2)利用JS自動請求授權頁面
這個方法相對而言比較笨拙,步驟略復雜,但目前能解決需求還沒有研究簡化方法,且由於頁面的跳轉多數情況下訪問頁面的時間會增加,但相比於前一個方法,該方法可以獲取到非關注用戶的基本信息。有些程序可能涉及到頁面分享,程序沒有強制關注但其他用戶通過分享直接進入頁面也需要記錄用戶信息,此時可以考慮該方法。(微信開發相關的代碼博主封裝成工具類調用,這裡先貼用到的部分,以後整理完成會全部貼出來並附下載鏈接)
該方法的思路為:js請求鏈接獲取code ---> 利用code換取openid ---> 得到用戶基本信息
a. 編輯配置
為了方便把用到的一些微信參數單獨寫入一個類,方便修改添加及調用
1 <?php 2 namespace common\tools\wechat; 3 4 /** 5 * 微信請求相關配置類庫 6 */ 7 class ConfigTool { 8 9 /** 10 * 微信配置參數 11 * @return array 配置參數 12 */ 13 public function setConfig() { 14 15 // 用於驗證微信接口配置信息的Token,可以任意填寫 16 $config['token'] = '自己的token'; 17 18 // appID 19 $config['appid'] = '自己的appid'; 20 21 // appSecret 22 $config['secret'] = '自己的secret'; 23 24 // 回調鏈接地址 25 $config['redirect_uri'] = 'http://tx.heivr.com/index.php?'; 26 27 // 是否以 HTTPS 安全協議訪問接口 28 $config['https_request'] = false; 29 30 // 授權作用域,snsapi_base (不彈出授權頁面,直接跳轉,只能獲取用戶openid), 31 // snsapi_userinfo (彈出授權頁面,可通過openid拿到昵稱、性別、所在地。並且, 32 // 即使在未關注的情況下,只要用戶授權,也能獲取其信息) 33 $config['scope'] = 'snsapi_userinfo'; 34 35 // 語言 36 $config['lang'] = 'zh_CN'; // zh_CN 簡體,zh_TW 繁體,en 英語 37 38 // 微信公眾賬戶授權地址 39 $config['mp_authorize_url'] = 'https://api.weixin.qq.com/cgi-bin/token'; 40 // 微信公眾賬戶js臨時票據地址 41 $config['jsapi_ticket_url'] = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket'; 42 // 授權地址 43 $config['authorize_url'] = 'https://open.weixin.qq.com/connect/oauth2/authorize'; 44 // 獲取access token 的地址 45 $config['access_token_url'] = 'https://api.weixin.qq.com/sns/oauth2/access_token'; 46 // 刷新 token 的地址 47 $config['refresh_token_url'] = 'https://api.weixin.qq.com/sns/oauth2/refresh_token'; 48 // 獲取用戶信息地址 49 $config['userinfo_url'] = 'https://api.weixin.qq.com/sns/userinfo'; 50 // 驗證access token 51 $config['valid_token_url'] = 'https://api.weixin.qq.com/sns/auth'; 52 // 上傳臨時素材地址 53 $config['media_temp_upload_url'] = 'https://api.weixin.qq.com/cgi-bin/media/upload?'; 54 // 上傳永久素材地址 55 $config['media_forever_upload_url'] = 'https://api.weixin.qq.com/cgi-bin/material/add_material?'; 56 57 return $config; 58 59 } 60 }
b. https請求工具
1 <?php 2 namespace common\tools; 3 4 /** 5 * https請求相關類庫 6 */ 7 class HttpsTool { 8 9 const TIMEOUT = 5; // 設置超時時間 10 11 private $ch; // curl對象 12 13 /** 14 * 發送curl請求,並獲取請求結果 15 * @param string 請求地址 16 * @param array 如果是post請求則需要傳入請求參數 17 * @param string 請求方法,get 或者 post, 默認為get 18 * @param bool 是否以https協議請求 19 */ 20 public function send_request($requests, $params = null, $method = 'get', $https = true) { 21 // 以get方式提交 22 if ($method == 'get') { 23 if($params){ 24 $request = $requests . $this->create_url($params); 25 }else{ 26 $request = $requests; 27 } 28 }else{ 29 $request = $requests; 30 } 31 32 $this->ch = curl_init($request); 33 curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);// 設置不顯示結果,儲存入變量 34 curl_setopt($this->ch, CURLOPT_TIMEOUT, self::TIMEOUT); // 設置超時限制防止死循環 35 36 // 判斷是否以https方式訪問 37 if ($https) { 38 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0); // 對認證證書來源的檢查 39 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0); // 從證書中檢查SSL加密算法是否存在 40 } 41 42 if ($method == 'post') { // 以post方式提交 43 //curl_setopt($this->ch, CURLOPT_SAFE_UPLOAD, false); //php 5.6文件上傳必加內容,5.4不需要 44 curl_setopt($this->ch, CURLOPT_POST, 1); // 發送一個常規的Post請求 45 curl_setopt($this->ch, CURLOPT_POSTFIELDS, $params); // Post提交的數據包 46 curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1); 47 } 48 49 $tmpInfo = curl_exec($this->ch); // 執行操作 50 if (curl_errno($this->ch)) { 51 echo 'Errno:'.curl_error($this->ch);//捕抓異常 52 } 53 curl_close($this->ch); // 關閉CURL會話 54 //var_dump($tmpInfo);exit; 55 return $tmpInfo; // 返回數據 56 } 57 58 /** 59 * 生成url 60 */ 61 public function create_url($data) { 62 $temp = '?'; 63 foreach ($data as $key => $item) { 64 $temp = $temp . $key . '=' . $item . '&'; 65 } 66 return substr($temp, 0, -1); 67 } 68 }
關於curl_setopt($this->ch, CURLOPT_SAFE_UPLOAD, false)會在微信圖片資源上傳博文中詳細講述它出現的心酸史,這裡暫時用不到,不做解釋
c. 授權基類
1 <?php 2 namespace common\tools\wechat; 3 4 use common\tools\wechat\ConfigTool; 5 use common\tools\HttpsTool; 6 /** 7 * Weixin_oauth 類庫 8 */ 9 class OauthTool { 10 11 public $conf; 12 13 public function __construct(){ 14 $re = new ConfigTool; 15 $this->conf = $re->setConfig(); 16 } 17 18 /** 19 * 生成用戶授權的地址 20 * @param string 自定義需要保持的信息 21 * @param sting 請求的路由 22 * @param bool 是否是通過公眾平台方式認真 23 */ 24 public function authorize_addr($route, $state='', $mp=false) { 25 26 if ($mp) { 27 $data = [ 28 'appid' => $this->conf['appid'], 29 'secret' => $this->conf['token'], 30 'grant_type' => 'client_credential' 31 ]; 32 $url = $this->conf['mp_authorize_url']; 33 } else { 34 $data = [ 35 'appid' => $this->conf['appid'], //公眾號唯一標識 36 'redirect_uri' => urlencode($this->conf['redirect_uri'] . $route), //授權後重定向的回調鏈接地址 37 'response_type' => 'code', //返回類型,此處填寫code 38 'scope'=>$this->conf['scope'], //應用授權作用域 39 'state'=>$state, //重定向後帶上state參數,開發者可以填寫任意參數 40 '#wechat_redirect'=>'' //直接在微信打開鏈接,可不填,做頁面302重定向時必須帶此參數 41 ]; 42 $url = $this->conf['authorize_url']; 43 } 44 45 $send = new HttpsTool; 46 //var_dump($url . $send->create_url($data));exit; 47 return $url . $send->create_url($data); 48 } 49 50 /** 51 * 獲取 access token 52 * @param string 用於換取access token的code,微信提供 53 */ 54 public function access_token($code) { 55 56 $data = [ 57 'appid' => $this->conf['appid'], 58 'secret' => $this->conf['secret'], 59 'code' => $code, 60 'grant_type' => 'authorization_code' 61 ]; 62 // 生成授權url 63 $url = $this->conf['access_token_url']; 64 65 $send = new HttpsTool; 66 return $send->send_request($url, $data); 67 } 68 69 /** 70 * 獲取用戶信息 71 * @param string access token 72 * @param string 用戶的open id 73 */ 74 public function userinfo($token, $openid) { 75 76 $data = [ 77 'access_token' => $token, 78 'openid' => $openid, 79 'lang' => $this->conf['lang'] 80 ]; 81 // 生成授權url 82 $url = $this->conf['userinfo_url']; 83 84 $send = new HttpsTool; 85 return $send->send_request($url, $data); 86 } 87 88 }
d. 授權基類調用及用戶數據處理(在控制器調用前,先對用戶數據存入或更新)
1 <?php 2 namespace wechat\controllers\classes; 3 4 use common\tools\wechat\OauthTool; 5 use common\models\User; 6 use common\tools\EmojiTool; 7 8 /** 9 * 微信用戶基本信息獲取 10 */ 11 class UserinfoClass { 12 13 /** 14 * 用戶授權並獲取code 15 * @return string 用戶code 16 */ 17 public function getCode($route, $state){ 18 19 $re = new OauthTool; 20 $request = $re->authorize_addr($route, $state); 21 $code = isset($_GET['code']) ? $_GET['code'] : ''; 22 23 return [$request,$code]; 24 } 25 26 /** 27 * 獲取用戶信息並寫入數據庫(之後加參數傳給code) 28 */ 29 public function info($code) { 30 $re = new OauthTool; 31 //獲取access token 32 $access = $re->access_token($code); 33 $token = json_decode($access,true); 34 //header("Content-type: text/html; charset=gbk"); 35 //獲取用戶信息 36 if(count($token) != 2) { 37 $response = $re->userinfo($token['access_token'], $token['openid']); 38 $user = json_decode($response,true); 39 //用戶昵稱轉換 40 //$user['nickname'] = EmojiTool::emoji_trans($user['nickname']); 41 42 if($model = User::findOne(['openid' => $user['openid'] ])) { //用戶已存在更新數據 43 $model->attributes = $user; 44 $model->modify_time = time(); 45 $model->save(false); 46 }else{ //用戶不存在寫入 47 $model = new User; 48 $model->attributes = $user; 49 $model->create_time = time(); 50 $model->save(false); 51 } 52 } 53 return isset($model->id) ? $model->id : ''; 54 } 55 56 }
e. 控制器調用(這裡只貼其中一個方法)
1 /** 2 * 產品列表 3 * @return object 所有可用產品信息 4 */ 5 public function actionIndex(){ 6 //判斷頁面是否自動刷新 7 if(isset($_GET['state'])) { 8 $refresh = 0; 9 }else{ 10 $refresh = 1; 11 } 12 13 //獲取用戶code 14 $user = new UserinfoClass; 15 $request = $user->getCode('r=store/index', 1); 16 17 //該用戶userid 18 $userid = $user->info($request[1]); 19 20 $model = new Product; 21 $list = $model->find()->where(['status' => 1])->all(); 22 23 return $this->render('index',['list' => $list, 'refresh' => $refresh, 'userid' => $userid, 'request' => $request]); 24 }
程序要求用戶打開產品列表即獲取用戶信息並存入數據庫,其中設計了幾個變量作用如下:
$refresh:判斷頁面是否刷新,由於首次打開頁面未進行oauth驗證時才自動請求驗證,避免反復刷新,這裡用回調的state參數作為判斷依據且設state=1(若有特定參數需要可將state賦值為所需值);
$request:即為驗證請求地址
f. 視圖自動刷新
只需要在視圖中添加以下js代碼即可
1 <script type="text/javascript"> 2 3 //自動請求獲取code 4 $(function(){ 5 var refresh = <?= $refresh; ?>; 6 var request = '<?= $request[0]; ?>'; 7 if(refresh == 1){ 8 console.log(1); 9 location = request; 10 } 11 }); 12 </script>
特此聲明:相關文章均為查閱資料、閱讀大神博文後結合實際開發情況遇到的問題整理而成,能找到原博的必會署名,找不到原博而引用的內容還望原博主海涵