使用 UniApp 實現小程式的微信登錄

  1. 微信登錄思路:

    1. 在main.js 中封裝公共函數,用於判斷用戶是否登錄
    2. 在main.js 中分定義全局變數,用於存儲介面地址
    3. 如果沒有登錄、則跳轉至登錄頁面
    4. 進入登錄頁面
    5. 通過 wx.login 獲取用戶的 code
    6. 通過 code 獲取用戶的 SessionKey、OpenId 等資訊【本應後台介面、但是此處使用js發送請求】
    7. 通過 openId 調用後台 Api 獲取用戶的資訊
    8. 獲取成功,則說明已經授權過了,直接登錄成功
    9. 獲取失敗,則說明沒有授權過,需要授權之後才能進行登錄
    10. 用戶點擊頁面微信登錄按鈕【 <button open-type="getUserInfo"></button>】
    11. 獲取用戶數據,然後調用後台介面寫入資料庫
  2. 在 applets/main.js 中添加如下

    // 封裝全局登錄函數
    // backpage, backtype 2個參數分別代表:
    // backpage : 登錄後返回的頁面
    // backtype : 打開頁面的類型[1 : redirectTo 2 : switchTab]
    Vue.prototype.checkLogin = function( backpage, backtype ){
    	// 同步獲取本地數據(uid、隨機碼、用戶名、頭像)
    	var user_id = uni.getStorageSync('user_id');
    	var user_nu = uni.getStorageSync('user_nu');
    	var user_nm = uni.getStorageSync('user_nm');
    	var user_fa = uni.getStorageSync('user_fa');
    	if( user_id == '' || user_nu == '' || user_fa == ''){
    		// 使用重定向的方式跳轉至登錄頁面
    		uni.redirectTo({url:'../login/login?backpage='+backpage+'&backtype='+backtype});
    		return false;
    	}
    	// 登錄成功、已經登錄返回數組 [用戶 id, 用戶隨機碼, 用戶昵稱, 用戶表情]
    	return [user_id, user_nu, user_nm, user_fa];
    }
    // 定義一個全局的請求地址
    Vue.prototype.apiServer = '//0608.cc/'
    
  3. 在 pages/login/login.vue 中添加如下

    <template>
    	<view>
    		<!-- login view html start -->
    		<view>
    			<view>
    				<view class="header"><image src="/static/img/public/login-wx.png"></image></view>
    				<view class="content">
    					<view>申請獲取以下許可權</view>
    					<text>獲得你的公開資訊(昵稱,頭像、地區等)</text>
    				</view>
    				<button class="bottom" type="primary" open-type="getUserInfo" withCredentials="true" lang="zh_CN" @getuserinfo="wxGetUserInfo">授權登錄</button>
    			</view>
    		</view>
    		<!-- login view html end -->
    	</view>
    </template>
    
    <script>
    export default {
    	data() {
    		return {
    			appid: '*************',
    			secret: '*************************',
    			code: '',
    			sessionKey: '',
    			openId: '',
    			userInfo: {
    				avatarUrl: '',
    				city: '',
    				country: '',
    				gender: 1,
    				language: '',
    				nickName: ''
    			},
    			pageOption: {}
    		};
    	},
    	methods: {
    		// 第一授權獲取用戶資訊 ===》按鈕觸發
    		wxGetUserInfo() {
    			let _self = this;
    			// 1.獲取用戶的資訊
    			uni.getUserInfo({
    				provider: 'weixin',
    				success: ( infoRes ) => {
    					console.log( infoRes )
    					_self.userInfo = infoRes.userInfo
    					// 2.提交數據到後台、寫入資料庫
    					uni.request({
    						url: _self.apiServer + 'appletsUserInfo',
    						data: {
    							openid: _self.openId,
    							avatarUrl: _self.userInfo.avatarUrl,
    							city: _self.userInfo.city,
    							country: _self.userInfo.country,
    							gender: _self.userInfo.gender,
    							language: _self.userInfo.language,
    							nickName: _self.userInfo.nickName
    						},
    						method: 'POST',
    						success: res => {
    							if( res.data.code != 0 )
    							{
    								uni.showToast({ title: res.data.msg, icon: 'none' });
    								return false;
    							}
    							// 用戶資訊寫入快取
    							uni.showToast({title: '登錄成功'})
    							uni.setStorageSync( 'user_id', res.data.res.u_id );
    							uni.setStorageSync( 'user_nm', res.data.res.u_nickName );
    							uni.setStorageSync( 'user_fa', res.data.res.u_avatarUrl );
    							uni.setStorageSync( 'user_nu', res.data.res.u_regtime );
    							// 然後跳回原頁面
    							if( _self.pageOption.backtype == 1 )
    							{
    								uni.redirectTo({ url: _self.pageOption.backpage })
    							}else{
    								uni.switchTab({ url: _self.pageOption.backpage })
    							}
    						},
    						fail: () => {
    							uni.showToast({ title: '用戶資訊操作失敗', icon: 'none' });
    						}
    					});
    				},
    				fail: () => {
    					uni.showToast({ title: '獲取用戶資訊失敗', icon: 'none' });
    				}
    			});
    			return false
    		},
    		// 登錄
    		login() {
    			let _self = this;
    
    			// 0. 顯示載入的效果
    			uni.showLoading({
    				title: '登錄中...'
    			});
    
    			// 1. wx 獲取登錄用戶 code
    			uni.login({
    				provider: 'weixin',
    				success: loginRes => {
    					console.log(loginRes);
    					_self.code = loginRes.code;
    					// 2. 將用戶登錄code傳遞到後台置換用戶SessionKey、OpenId等資訊
    					uni.request({
    						url:
    							'//api.weixin.qq.com/sns/jscode2session?appid=' +
    							_self.appid +
    							'&secret=' +
    							_self.secret +
    							'&js_code=' +
    							_self.code +
    							'&grant_type=authorization_code',
    						success: codeRes => {
    							console.log(codeRes);
    							_self.openId = codeRes.data.openid;
    							_self.sessionKey = codeRes.data.session_key;
    							// 3.通過 openId 判斷用戶是否授權
    							uni.request({
    								url: _self.apiServer + 'loginApplets',
    								data: {
    									openid: _self.openId
    								},
    								method: 'POST',
    								success: openIdRes => {
    									console.log(openIdRes);
    									// 隱藏loading
    									uni.hideLoading();
    									// 還沒授權登錄、請先授權然後登錄
    									if (openIdRes.data.code == 1) {
    										// 提示消息、讓用戶授權
    										uni.showToast({ title: openIdRes.data.msg, icon: 'none' });
    									}
    									// 已經授權了、查詢到用戶的數據了
    									if (openIdRes.data.code == 0) {
    										// 用戶資訊寫入快取
    										uni.showToast({title: '登錄成功'})
    										uni.setStorageSync( 'user_id', openIdRes.data.res.u_id );
    										uni.setStorageSync( 'user_nm', openIdRes.data.res.u_nickName );
    										uni.setStorageSync( 'user_fa', openIdRes.data.res.u_avatarUrl );
    										uni.setStorageSync( 'user_nu', openIdRes.data.res.u_regtime );
    										// 然後跳回原頁面
    										if( _self.pageOption.backtype == 1 )
    										{
    											uni.redirectTo({ url: _self.pageOption.backpage })
    										}else{
    											uni.switchTab({ url: _self.pageOption.backpage })
    										}
    									}
    								},
    								fail: () => {
    									uni.showToast({ title: '獲取授權資訊失敗', icon: 'none' });
    									return false;
    								}
    							});
    						},
    						fail: () => {
    							uni.showToast({ title: '獲取 SesssionKey OpenId 失敗', icon: 'none' });
    							return false;
    						}
    					});
    				},
    				fail: () => {
    					uni.showToast({ title: '獲取 code 失敗', icon: 'none' });
    					return false;
    				}
    			});
    			return false;
    		}
    	},
    	onLoad( options ) {
    		// 接收跳轉的參數
    		this.pageOption = options
    		//默認載入
    		this.login();
    	}
    };
    </script>
    
    <style>
    .header {
    	margin: 90rpx 0 90rpx 50rpx;
    	border-bottom: 1px solid #ccc;
    	text-align: center;
    	width: 650rpx;
    	height: 300rpx;
    	line-height: 450rpx;
    }
    
    .header image {
    	width: 200rpx;
    	height: 200rpx;
    }
    
    .content {
    	margin-left: 50rpx;
    	margin-bottom: 90rpx;
    }
    
    .content text {
    	display: block;
    	color: #9d9d9d;
    	margin-top: 40rpx;
    }
    
    .bottom {
    	border-radius: 80rpx;
    	margin: 70rpx 50rpx;
    	font-size: 35rpx;
    }
    </style>
    
  4. 在 pages/my/my.vue 中添加如下:

    <template>
    	<view>我的頁面</view>
    </template>
    
    <script>
    var loginRes;
    export default {
    	data() {
    		return {};
    	},
    	onLoad() {
    		// 載入定義好的方法
    		loginRes = this.checkLogin('../my/my', 2);
    		// 沒有登錄成功,返回空
    		if (!loginRes) {
    			return;
    		}
    	},
    	methods: {}
    };
    </script>
    
    <style></style>
    
  5. PHP 介面 loginApplets

    public function loginApplets(Request $request, UserInfo $userInfo)
    {
        // 獲取數據
        $data['u_openid'] = $request->param('openid', '');
        // 驗證數據
        $rule = [
            'u_openid' => 'require|max:200|min:10'
        ];
        $message = [
            'u_openid.require' => 'openid 不能為空',
            'u_openid.max'     => 'openid 格式錯誤',
            'u_openid.min'     => 'openid 格式錯誤'
        ];
        $validate = Validate::rule($rule)->message($message);
        if (!$validate->check($data)) {
            return json(['code' => 1, 'msg' => $validate->getError(), 'res' => null]);
        }
        // 根據 openid 判斷是否存在
        $where['u_openid'] = $data['u_openid'];
        $user = $userInfo->selOne($where);
        if (!$user) {
            return json(['code' => 1, 'msg' => '還沒授權登錄、請先授權然後登錄', 'res' => $user]);
        }
        return json(['code' => 0, 'msg' => '已授權獲取到用戶的數據', 'res' => $user]);
    }
    
  6. PHP 介面 appletsUserInfo

    public function appletsUserInfo(Request $request, UserInfo $userInfo)
    {
        // 獲取數據
        $data['u_openid'] = $request->param('openid', '');
        $data['u_avatarUrl'] = $request->param('avatarUrl', '');
        $data['u_city'] = $request->param('city', '');
        $data['u_country'] = $request->param('country', '');
        $data['u_gender'] = $request->param('gender', '');
        $data['u_language'] = $request->param('language', '');
        $data['u_nickName'] = $request->param('nickName', '');
        // 驗證數據
        $rule = [
            'u_openid' => 'require|max:200|min:10',
            'u_avatarUrl' => 'require',
            'u_nickName' => 'require'
        ];
        $message = [
            'u_openid.require'      => 'openid 不能為空',
            'u_openid.max'          => 'openid 格式錯誤',
            'u_openid.min'          => 'openid 格式錯誤',
            'u_avatarUrl.require'   => '用戶頭像 不能為空',
            'u_nickName.max'        => '用戶名 格式錯誤',
        ];
        $validate = Validate::rule($rule)->message($message);
        if (!$validate->check($data)) {
            return json(['code' => 1, 'msg' => $validate->getError(), 'res' => null]);
        }
    
        // 根據 openid 判斷是否存在
        $where['u_openid'] = $data['u_openid'];
        $user = $userInfo->selOne($where);
    
        // 存在、執行修改
        if ($user) {
            $user_res = $userInfo->updOne($where, $data);
            $res = [];
            $res['u_id'] = $user['u_id'];
            $res['u_regtime'] = $user['u_regtime'];
        }
    
        // 不存在、執行添加
        if (empty($user)) {
            $res = [];
            $res = $data;
            $res['u_regtime'] = time();
            $res['u_id'] = $userInfo->addOne($res);
        }
    
        // 判斷是否添加成功
        if (empty($res['u_id'])) {
            return json(['code' => 1, 'msg' => '註冊失敗,返回重試', 'res' => null]);
        }
        return json(['code' => 0, 'msg' => 'ok', 'res' => $res]);
    }
    
  7. 完工!!!