上一篇文章網站實現微信登錄之嵌入二維碼中描述了如何在自己的登錄頁面內嵌入登錄二維碼,今天的這篇文章主要是描述下在掃碼成功之後微信重定向回網站後登錄邏輯的處理,其實也就是驗證身份信息,授權用戶登錄的邏輯。這裡說句題外話,寫博客復習已經做過的項目真的有助於自己對已經寫過代碼和業務邏輯的理解,說不定還有意外的收獲。所謂,“溫故而知新”,我會保持寫博客的習慣。
1,微信掃碼成功之後
在用戶掃碼成功之後,pc端網站上的二維碼會出現如下的提示:(這裡是用的微信開發文檔中的例子1號店網站用來演示效果)。
header('Location: https://open.weixin.qq.com/connect/qrconnect?appid=wxbdc5610cc59c1631&redirect_uri=https%3A%2F%2Fpassport.yhd.com%2Fwechat%2Fcallback.do&response_type=code&scope=snsapi_login&state=3d6be0a4035d839573b04816624a415e#wechat_redirect');
在實際開發中,應該根據設計的要求或客戶需求來選擇利用js嵌入還是在頁面中重定向的方法。
然後,在手機端的微信中會彈出提示用戶確認登陸1號店的界面:
1 public function actionCallback($code, $state) 2 { 3 // 獲取並校驗前台存儲的隨機串,防csrf攻擊 4 $session = Yii::$app->session; 5 if ($state != $session->get('wx_state')) { 6 exit(); 7 } 8 $session->remove('wx_state'); 9 10 // 微信開放平台網站應用的appid和秘鑰secret 11 $appid = 'appid'; 12 $secret = 'secret'; 13 14 $curl = new Curl(); 15 $wxresponse = $curl->get('https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appid 16 . '&secret=' . $secret . '&code=' . $code . '&grant_type=authorization_code'); 17 $wxresult = json_decode($wxresponse);
注意:上面代碼第14行用到了一個yii2的curl擴展,感興趣的可以去github上看下。
第15行請求成功後返回的參數如下:
{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE" }
參數 說明 access_token 接口調用憑證 expires_in access_token接口調用憑證超時時間,單位(秒) refresh_token 用戶刷新access_token openid 授權用戶唯一標識 scope 用戶授權的作用域,使用逗號(,)分隔
actionCallback方法的參數中,$code是用戶掃碼確認登錄後微信返回的,$state是自己設置的參數,詳細可見網站實現微信登錄之嵌入二維碼一文。請求得到access_token和openid最後被存儲到$wxresult中。
這裡需要注意下,此處返回的openid與第3節中返回的openid是不同的,注意看參數說明。
3,利用上面獲取的token和openid獲取用戶的個人信息,查詢數據庫驗證登錄。成功則設置session,不成功(新用戶),可以考慮添加新用戶實現掃碼注冊的邏輯。
1 if (isset($wxresult->errcode) && $wxresult->errcode > 0) { 2 // 向微信請求授權時出錯,打印錯誤碼 3 echo json_encode($wxresult); 4 exit; 5 } else { 6 // 獲取用戶個人信息(unionid) 7 $response = $curl->get('https://api.weixin.qq.com/sns/userinfo?access_token=' . $wxresult->access_token 8 . '&openid=' . $wxresult->openid); 9 $result = json_decode($response); 10 $wxUser = WxUser::find()->where(['wx_unionid' => $result->unionid])->one(); 11 if ($wxUser) { 12 // 登錄 13 $result = Yii::$app->user->login($wxUser->id, 3600 * 24 * 30); 14 if ($result) { 15 // 登錄成功,設置session 16 Yii::$app->session['wxuser'] = $wxUser->id; 17 } else { 18 // 登錄失敗 19 echo 'login 失敗'; 20 exit(); 21 } 22 } else { 23 // 創建新用戶 24 } 25 }
第7行請求成功後,返回的參數示例如下:
{ "openid":"OPENID", "nickname":"NICKNAME", "sex":1, "province":"PROVINCE", "city":"CITY", "country":"COUNTRY", "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0", "privilege":[ "PRIVILEGE1", "PRIVILEGE2" ], "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" }
參數 說明 openid 普通用戶的標識,對當前開發者帳號唯一 nickname 普通用戶昵稱 sex 普通用戶性別,1為男性,2為女性 province 普通用戶個人資料填寫的省份 city 普通用戶個人資料填寫的城市 country 國家,如中國為CN headimgurl 用戶頭像,最後一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),用戶沒有頭像時該項為空 privilege 用戶特權信息,json數組,如微信沃卡用戶為(chinaunicom) unionid 用戶統一標識。針對一個微信開放平台帳號下的應用,同一用戶的unionid是唯一的。
上面代碼第10行是依據用戶的unionid是否在數據庫中來驗證其是否為網站的可登錄用戶,這裡為什麼要使用用戶的unionid呢?這裡涉及到了微信授權接口的UnionID機制。
微信開發文檔授權後接口調用(UnionID)中對於unionid的解釋:此接口用於獲取用戶個人信息。開發者可通過OpenID來獲取用戶基本信息。特別需要注意的是,如果開發者擁有多個移動應用、網站應用和公眾帳號,可通過獲取用戶基本信息中的unionid來區分用戶的唯一性,因為只要是同一個微信開放平台帳號下的移動應用、網站應用和公眾帳號,用戶的unionid是唯一的。換句話說,同一用戶,對同一個微信開放平台下的不同應用,unionid是相同的。
我的理解是,每個用戶對各公眾號的OpenID是唯一的,而對於不同公眾號,同一用戶的openid是不同的。那麼如果有多個公眾號(服務號、訂閱號),用openid就不能區分用戶的唯一身份,就只能用unionid了。就算只有一個公眾號,也建議采用unionid來區分用戶身份的唯一性。
4,小結
微信掃碼登錄或注冊方便了用戶,但卻增加了開發者的開發、調試等工作量。其實不止是開發階段的工作量,整個網站在設計時就要考慮到這個功能,登錄界面的設計、頁面的跳轉等。
參考:
微信開放平台資源中心
完