一、简介:
微信小程序登录这个概念有些广,有调用官方登录接口和调用开发者第三方服务器登录两种。为什么要登录?因为小程序用户从微信打开时,就已经登录,但是登录后,对于开发者来说还是未登录状态,没有一个唯一固定的标识,无法保存用户的信息,用户第二次进入小程序不能保持上次登录时的数据、状态。
关于登录流程,我这边从网上拿的一张图,虽然不是很认同里面的说法,但流程大致如此:
其中,第三方服务器就是开发者自己的服务器了,比如我的:https://wx.pukuimin.top (测试服务器,暂时只购买一年)
二、登录功能开发
虽然功能看起来简单,但从无到有做出来,还是要花些时间,重要的没做出来之前感觉不太好理解,下面着手开发登录功能,小程序服务端用的是.Net WEB API开发。
1、小程序客户端
1.1 在app.json 添加页面
"pages/test/test"
保存后,在pages下面,会生成test目录,然后下面有 test.js,test.json,test.wxml,test.wxss四个文件
1.2 修改config.js
var host = 'https://wx.pukuimin.top';
var config = {
// 下面的地址配合云端 Demo 工作
service: {
host,
// 登录地址,用于建立会话
getUserInfoUrl: `${host}/WeiXin/GetUserInfo`,
}
};1.3 引用的腾讯云demo代码
/vendor/wafer2-client-sdk/lib/login.js 我稍微改了一下,导出 getWxLoginResult 方法,在外部要调用
var utils = require('./utils');
var constants = require('./constants');
var Session = require('./session');
/***
* @class
* 表示登录过程中发生的异常
*/
var LoginError = (function () {
function LoginError(type, message) {
Error.call(this, message);
this.type = type;
this.message = message;
}
LoginError.prototype = new Error();
LoginError.prototype.constructor = LoginError;
return LoginError;
})();
/**
* 微信登录,获取 code 和 encryptData
*/
var getWxLoginResult = function getLoginCode(callback) {
wx.login({
success: function (loginResult) {
console.log("loginResult:");
console.log(loginResult);
wx.getUserInfo({
success: function (userResult) {
console.log("userResult:");
console.log(userResult);
callback(null, {
code: loginResult.code,
encryptedData: userResult.encryptedData,
iv: userResult.iv,
userInfo: userResult.userInfo,
});
},
fail: function (userError) {
var error = new LoginError(constants.ERR_WX_GET_USER_INFO, '获取微信用户信息失败,请检查网络状态');
error.detail = userError;
callback(error, null);
},
});
},
fail: function (loginError) {
var error = new LoginError(constants.ERR_WX_LOGIN_FAILED, '微信登录失败,请检查网络状态');
error.detail = loginError;
callback(error, null);
},
});
};
var noop = function noop() {};
var defaultOptions = {
method: 'GET',
success: noop,
fail: noop,
loginUrl: null,
};
/**
* @method
* 进行服务器登录,以获得登录会话
*
* @param {Object} options 登录配置
* @param {string} options.loginUrl 登录使用的 URL,服务器应该在这个 URL 上处理登录请求
* @param {string} [options.method] 请求使用的 HTTP 方法,默认为 "GET"
* @param {Function} options.success(userInfo) 登录成功后的回调函数,参数 userInfo 微信用户信息
* @param {Function} options.fail(error) 登录失败后的回调函数,参数 error 错误信息
*/
var login = function login(options) {
options = utils.extend({}, defaultOptions, options);
if (!defaultOptions.loginUrl) {
options.fail(new LoginError(constants.ERR_INVALID_PARAMS, '登录错误:缺少登录地址,请通过 setLoginUrl() 方法设置登录地址'));
return;
}
var doLogin = () => getWxLoginResult(function (wxLoginError, wxLoginResult) {
if (wxLoginError) {
options.fail(wxLoginError);
return;
}
var userInfo = wxLoginResult.userInfo;
// 构造请求头,包含 code、encryptedData 和 iv
var code = wxLoginResult.code;
var encryptedData = wxLoginResult.encryptedData;
var iv = wxLoginResult.iv;
var header = {};
header[constants.WX_HEADER_CODE] = code;
header[constants.WX_HEADER_ENCRYPTED_DATA] = encryptedData;
header[constants.WX_HEADER_IV] = iv;
// 请求服务器登录地址,获得会话信息
wx.request({
url: options.loginUrl,
header: header,
method: options.method,
data: options.data,
success: function (result) {
var data = result.data;
// 成功地响应会话信息
if (data && data.code === 0 && data.data.skey) {
var res = data.data
if (res.userinfo) {
Session.set(res.skey);
options.success(userInfo);
} else {
var errorMessage = '登录失败(' + data.error + '):' + (data.message || '未知错误');
var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, errorMessage);
options.fail(noSessionError);
}
// 没有正确响应会话信息
} else {
var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, JSON.stringify(data));
options.fail(noSessionError);
}
},
// 响应错误
fail: function (loginResponseError) {
var error = new LoginError(constants.ERR_LOGIN_FAILED, '登录失败,可能是网络错误或者服务器发生异常');
options.fail(error);
},
});
});
var session = Session.get();
if (session) {
wx.checkSession({
success: function () {
options.success(session.userInfo);
},
fail: function () {
Session.clear();
doLogin();
},
});
} else {
doLogin();
}
};
var setLoginUrl = function (loginUrl) {
defaultOptions.loginUrl = loginUrl;
};
module.exports = {
LoginError: LoginError,
login: login,
setLoginUrl: setLoginUrl,
getWxLoginResult: getWxLoginResult
};/utils/util.js
const formatTime = date => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
const formatNumber = n => {
n = n.toString()
return n[1] ? n : '0' + n
}
// 显示繁忙提示
var showBusy = text => wx.showToast({
title: text,
icon: 'loading',
duration: 10000
})
// 显示成功提示
var showSuccess = text => wx.showToast({
title: text,
icon: 'success'
})
// 显示失败提示
var showModel = (title, content) => {
wx.hideToast();
wx.showModal({
title,
content: JSON.stringify(content),
showCancel: false
})
}
module.exports = { formatTime, showBusy, showSuccess, showModel }1.4 test.wxml
<!--pages/test/test.wxml-->
<view class="container">
<!-- 用户登录测试 -->
<view class="userinfo" catchtap="catch_logout">
<image class="userinfo-avatar" src="{{logged ? userInfo.avatarUrl : '../index/user-unlogin.png'}}" background-size="cover"></image>
<view>
<text bindtap="login" id="login" data-testinfo="logintest" class="userinfo-nickname">{{logged ? userInfo.nickName : '点击登录'}}</text>
<text class="userinfo-nickname" wx:if="{{logged}}">{{userInfo.lastLoginTime}}</text>
</view>
<button id="logout" class="{{logged ?'show':'hide'}} userinfo-logout" bindtap="logout">退出</button>
</view>
</view>1.5 test.wxss
/* pages/test/test.wxss */
page {
background: #F6F6F6;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.userinfo, .uploader, .tunnel {
margin-top: 40rpx;
height: 140rpx;
width: 100%;
background: #FFF;
border: 1px solid rgba(0, 0, 0, .1);
border-left: none;
border-right: none;
display: flex;
flex-direction: row;
align-items: center;
transition: all 300ms ease;
}
.userinfo-avatar {
width: 100rpx;
height: 100rpx;
margin: 20rpx;
border-radius: 50%;
}
.userinfo-nickname {
font-size: 32rpx;
color: #007AFF;
}
.hide{
display:none;
}
.show{
display:block;
}
.userinfo-logout{
font-size: 32rpx;
width: 130rpx;
line-height: 60rpx;
}1.6 test.js
// pages/test/test.js
var login = require('../../vendor/wafer2-client-sdk/lib/login.js')
var config = require('../../config.js')
var util = require('../../utils/util.js')
// This is our data.
var helloData = {
name: 'WeChat',
userInfo: {},
logged: false,
}
Page({
/**
* 页面的初始数据
*/
data: helloData,
changeName: function (e) {
// sent data change to view
this.setData({
name: 'MINA'
});
},
login:function(e){
if (this.data.logged) return;
console.log('begin login');
var context = this;
var doLogin = () => login.getWxLoginResult(function (wxLoginError, wxLoginResult) {
console.log(wxLoginError);
console.log(wxLoginResult);
if (wxLoginError && wxLoginError.detail) {
console.log("login fail," + wxLoginError.detail.errMsg);
wx.openSetting({ success: (res) => { console.log(res); } });//登录时,如果拒绝,短时间内不会弹出登录窗口,也一直登录不上,要调用此接口重新设置权限才行
return;
}
console.log('begin getuserinfo');
//options = utils.extend({}, defaultOptions, options);
var noop = function noop() { };
var options = {
method: 'post',
success: noop,
fail: noop,
getUserInfoUrl: config.service.getUserInfoUrl,
data: wxLoginResult
};
//util.showBusy('正在登录')
// 请求服务器登录地址,获得会话信息
wx.request({
url: options.getUserInfoUrl,
//header: header,
method: options.method,
data: options.data,
success: function (result) {
var data = result.data;
console.log("request result");
console.log(result);
util.showSuccess('登录成功')
// 成功地响应会话信息
if (data && data.errcode === '0' && data.data.skey) {
context.setData({
userInfo: data.data,
logged: true
});
wx.setStorageSync('userInfo', data.data);//保存到本地缓存
//Session.set(res.skey);
}
},
// 响应错误
fail: function (loginResponseError) {
//var error = new login.LoginError(constants.ERR_LOGIN_FAILED, '登录失败,可能是网络错误或者服务器发生异常');
//options.fail(error);
console.log(loginResponseError);
util.showModel('请求失败', loginResponseError)
},
});
console.log('end getuserinfo');
});
doLogin();
console.log('end login');
},
logout:function(e){
if (this.data.logged){
this.setData({
userInfo: null,
logged: false
});
wx.removeStorageSync("userInfo");
}
},
catch_logout:function(e){
console.log("catch_logout");
//console.log(e);
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var info = wx.getStorageSync('userInfo');//获取本地缓存
if(info&& info!=undefined){//判断登录信息有没有存在,有没有过期等逻辑
//没有过期直接登录,不需要再次登录
this.setData({
userInfo: info,
logged: true
});
console.log(info);
console.log("exist logininfo")
}
else{
console.log("no logininfo");
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面