Laravel5.5結合小程式獲取使用者unionid | 網站實現微信掃碼登入
一、背景
我們目前有一個專案是有兩個終端,一個是小程式端,一個PC網頁端,而我們設計這個產品的使用者模組是直接和使用者微信賬號繫結,即同一個微信,可以訪問小程式和PC端。而這兩個終端需要一個唯一的標識來匹配使用者的身份,微信裡的使用者唯一標識也就是“unionid”(注意不同終端的openid是不同的)。
而小程式端不能直接獲取到明文的unionid,只能通過wx.getUserInfo獲取到加密資料,所以需要先在小程式端獲取到unionid的加密資料後,再傳給服務端解密。
二、微信開放平臺繫結小程式
在程式碼工作前還需要執行一步準備工作。
到微信開放平臺,管理中心->小程式->繫結小程式,繫結要獲取使用者unionid的小程式。
三、小程式端程式碼
根據微信小程式手冊,小程式要獲取微信使用者的unionid需要呼叫wx.getUserInfo,而要喚起使用者授權,必須通過button元件,即是使用者必須點選某個按鈕才能觸發到呼叫wx.getUserInfo。
1. wxml的按鈕程式碼
<button disabled='{{button.disabled}}' type='{{button.btn_type}}' bindtap='{{button.bindtap}}' open-type="{{button.openType}}" form-type="submit"> {{button.content}}</button>
2. js程式碼
var app = getApp(); /** * 頁面的初始資料 */ data: { button: { btn_type: 'confirm', content: '點選獲取', isExist: false, bindtap: 'bindGetUserInfo', openType: 'getUserInfo' } }, bindGetUserInfo(e) { wx.login({ success: function(res) { var code = res.code wx.getUserInfo({ withCredentials: true, success: function(res) { app.sendRequest({ url: "/decrypt", method: "POST", data: { code: code, iv: res.iv, encryptedData: res.encryptedData, rawData: res.rawData, signature: res.signature }, success: function(res) { if (res.code == -1) { // 解密失敗,重新執行 this.bindGetUserInfo() } else if (res.code == 0) { // 解密成功後的操作…… } }, }) }, }) } }) },
四、服務端解密程式碼
服務端的程式碼主要用於解密操作,而在Laravel框架中,可以用composer安裝一個解密包,具體操作如下:
1. 在根目錄下的composer.json檔案中的require陣列中新增擴充套件元件:
"require": {
"frowhy/mini-program-aes": "^1.1"
},
2. 到專案根目錄下(windows開啟dos系統跳轉到專案根目錄),執行命令:
composer update
3. 控制器中引用擴充套件類並解密
use EasyWeChat\Factory;
use Illuminate\Http\Request;
use Leto\MiniProgramAES\WXBizDataCrypt;
private function wxDecrypt(Request $request)
{
$data = $request->validate([
'iv' => 'required',
'code' => 'required',
'rawData' => 'required',
'signature' => 'required',
'encryptedData' => 'required'
])
// 此處使用了EasyWeChat,這個需要額外安裝
$miniProgram = Factory::miniProgram([
'app_id' => APP_ID, // 小程式的APPID
'secret' => APP_SECRET // 小程式的APP_SECRET
]);
// 獲取session_key
$result = $miniProgram->auth->session($code);
// 驗籤
if ($signature != sha1($rawData. $result['session_key'])) {
return ['code' => -1, 'msg' => '驗籤失敗'];
}
// 例項化解密類
$pc = new WXBizDataCrypt($app->app_id, $result['session_key']);
// 解密
$errCode = $pc->decryptData($encryptedData, $iv, $reData);
// 判斷解密是否成功
if ($errCode == 0) {
return [
'code' => 0,
'msg' => 'ok',
'data' => json_decode($reData, true)
];
}
return return ['code' => -2, 'msg' => '解密失敗'];
}
注意:
2. 微信解密會存在失敗的可能性,所以需要返回解密失敗的狀態給前端,前端判斷失敗重新請求就好。
4. 解密後的資料參考如下:
{
"openId": "openId",
"nickName": "test",
"gender": 2,
"language": "zh_CN",
"city": "Handan",
"province": "Hebei",
"country": "China",
"avatarUrl": "頭像檔案地址",
"unionId": "unionId",
"watermark": {
"timestamp": 1541477070,
"appid": "appId"
}
}
五、微信掃碼登入程式碼
1. 到微信開放平臺註冊網站應用,申請後一般3個工作日左右就可以稽核通過。
2. 生成微信掃碼二維碼介面
在開放平臺申請網站應用過程中會配置一個網站授權回撥域,需要配置即將做掃碼登入的網站網址,申請成功後,能夠獲得一個APP_ID和自己生成一個APP_SECRET。
public function login(Request $request)
{
// 判斷是否已登入
if (session('user')) {
return redirect('index');
}
// 網站應用配置資訊
$config = ['app_id'=>APP_ID, 'app_secret'=>APP_SECRET];
// 設定使用者掃碼同意後自動跳轉的地址
$redirectUrl = UrlEncode("https://www.example.com/oauth");
// 設定用於驗證的隨機數
$state = str_random(20);
$request->session()->flash('state', $state);
// 組裝地址
$url = "https://open.weixin.qq.com/connect/qrconnect?appid={$config['app_id']}&redirect_uri={$redirectUrl}&response_type=code&scope=snsapi_login&state={$state}#wechat_redirect";
// 跳轉至掃碼地址頁面
return redirect($url);
}
生成的頁面結果如下:
3. 跳轉到設定好的同步地址的驗證程式碼
public function oauth(Request $request)
{
$data = $request->validate([
'code' => 'required',
'state' => 'required'
]);
// 驗證state
if ($data['state'] != session('state')) {
return "請勿違法操作";
}
// 網站應用配置資訊
$config = ['app_id'=>APP_ID, 'app_secret'=>APP_SECRET];
// 獲取unionid
$result = file_get_contents("https://api.weixin.qq.com/sns/oauth2/access_token?appid={$config['app_id']}&secret={$config['app_secret']}&code={$data['code']}&grant_type=authorization_code");
$result = json_decode($result, true);
if (!empty($result['errcode'])) {
return "掃碼登入錯誤,錯誤碼:{$result['errcode']}";
}
// 根據unionid獲取使用者資訊
$info = $this->getUserInfo($result['unionid']);
// 將使用者資訊儲存在session中
$request->session()->put('user', $info['data']);
// 跳轉至網站首頁
return redirect('index');
}