11
2018
03

微信小程序开发-用户登录流程学习

一、简介:

微信小程序登录这个概念有些广,有调用官方登录接口和调用开发者第三方服务器登录两种。为什么要登录?因为小程序用户从微信打开时,就已经登录,但是登录后,对于开发者来说还是未登录状态,没有一个唯一固定的标识,无法保存用户的信息,用户第二次进入小程序不能保持上次登录时的数据、状态。

关于登录流程,我这边从网上拿的一张图,虽然不是很认同里面的说法,但流程大致如此:

其中,第三方服务器就是开发者自己的服务器了,比如我的: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 () {
},
/**
   * 生命周期函数--监听页面显示
   */
onShow: function () {
},
/**
   * 生命周期函数--监听页面隐藏
   */
onHide: function () {
},
/**
   * 生命周期函数--监听页面卸载
   */
onUnload: function () {
},
/**
   * 页面相关事件处理函数--监听用户下拉动作
   */
onPullDownRefresh: function () {
},
/**
   * 页面上拉触底事件的处理函数
   */
onReachBottom: function () {
},
/**
   * 用户点击右上角分享
   */
onShareAppMessage: function () {
}
})


1.7 test.json 设置小程序页面标题

{
"navigationBarTitleText": "小程序测试"
}


2、小程序服务端

2.1 新建.net web api应用程序

由于是新项目,所以新建web应用程序,要简单配置下

App_Start/WebApiConfig.cs  修改默认路由配置

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

添加log4net包(版本 2.0.8)然后配置,这里不是重点。

2.2 新建控制器、登录action逻辑

在Controllers里,新建控制器 WeiXinController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using ypsuit.api.WXHelper;
using ypsuit.common;
using ypsuit.model.WeiXinModel;
namespace ypsuit.api.Controllers
{
    public class WeiXinController : ApiController
    {
        [HttpPost]
        public ReturnModel<GetUserInfoOutputModel> GetUserInfo(GetUserInfoInputModel input)
        {
            Log4Logger.Info(new LogContent()
            {
                Url = HttpContext.Current.Request.RawUrl,
                LogIp = DataHelper.GetIP(),
                LogDescription = "参数:" + JsonUtility.ToJson(input)
            });
            var temp = new WeiXinUsersHelper().GetUserData(input);
            return temp;
        }
    }
}


ReturnModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ypsuit.model.WeiXinModel
{
    public class ReturnModel
    {
        public ReturnModel()
        {
            errcode = "0";
            errmsg = "";
        }
        public string errcode { get; set; }
        public string errmsg { get; set; }
    }
    public class ReturnModel<T>
    {
        public ReturnModel()
        {
            errcode = "0";
            errmsg = "";
        }
        public string errcode { get; set; }
        public string errmsg { get; set; }
        public T data { get; set; }
    }
}

GetUserInfoInputModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ypsuit.model.WeiXinModel
{
    public class GetUserInfoInputModel
    {
        /// <summary>
        /// wx.login客户端接口能得到的信息
        /// </summary>
        public string code { get; set; }
        /// <summary>
        /// wx.getUserInfo客户端接口能得到的信息
        /// </summary>
        public string encryptedData { get; set; }
        /// <summary>
        ///  wx.getUserInfo客户端接口能得到的信息
        /// </summary>
        public string iv { get; set; }
    }
}

GetUserInfoOutputModel.cs

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ypsuit.model.WeiXinModel
{
    public class GetUserInfoOutputModel
    {
        public GetUserInfoOutputModel()
        {
            ExpTime = DateTime.Now.AddDays(1).ToString("yyyy-MM-dd HH:mm:ss");//如果不指定,默认1天有效期
        }
        /// <summary>
        /// 用户固定的openID
        /// </summary>
        [JsonIgnore]
        public string openid { get; set; }
        /// <summary>
        /// 用户数据解密的AesKey,每次都可能不同
        /// </summary>
        [JsonIgnore]
        public string session_key { get; set; }
        /// <summary>
        /// md5(openid)
        /// </summary>
        public string skey { get; set; }
        /// <summary>
        /// 过期时间
        /// </summary>
        public string ExpTime { get; set; }
        /// <summary>
        /// md5(skey + ExpTime),验证合法性
        /// </summary>
        public string skeyCheckCode { get; set; }
        /// <summary>
        /// 用户昵称
        /// </summary>
        public string nickName { get; set; }
        /// <summary>
        /// 头像url
        /// </summary>
        public string avatarUrl { get; set; }
    }
}

WeiXinUsersHelper.cs

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using ypsuit.common;
using ypsuit.model.WeiXinModel;
namespace ypsuit.api.WXHelper
{
    public class WeiXinUsersHelper
    {
        #region 获取Get请求链接返回的html数据 
        /// <summary> 
        /// 获取Get请求链接返回的html数据 
        /// </summary> 
        /// <param name="Url">链接</param> 
        /// <param name="type">请求编码</param> 
        /// <returns></returns> 
        public string GetUrltoHtml(string Url, string type = "utf-8")
        {
            try
            {
                System.Net.WebRequest wReq = System.Net.WebRequest.Create(Url);
                // Get the response instance. 
                System.Net.WebResponse wResp = wReq.GetResponse();
                System.IO.Stream respStream = wResp.GetResponseStream();
                // Dim reader As StreamReader = New StreamReader(respStream) 
                using (System.IO.StreamReader reader = new System.IO.StreamReader(respStream, Encoding.GetEncoding(type)))
                {
                    return reader.ReadToEnd();
                }
            }
            catch (System.Exception ex)
            {
                return ex.Message;
            }
        } 
        #endregion
        #region 微信小程序用户数据解密 
        public static string AesKey;
        public static string AesIV;
        /// <summary> 
        /// AES解密 
        /// </summary> 
        /// <param name="inputdata">输入的数据encryptedData</param> 
        /// <param name="AesKey">key</param> 
        /// <param name="AesIV">向量128</param> 
        /// <returns name="result">解密后的字符串</returns> 
        public string AESDecrypt(string inputdata)
        {
            try
            {
                AesIV = AesIV.Replace(" ", "+");
                AesKey = AesKey.Replace(" ", "+");
                inputdata = inputdata.Replace(" ", "+");
                byte[] encryptedData = Convert.FromBase64String(inputdata);
                RijndaelManaged rijndaelCipher = new RijndaelManaged();
                rijndaelCipher.Key = Convert.FromBase64String(AesKey); // Encoding.UTF8.GetBytes(AesKey); 
                rijndaelCipher.IV = Convert.FromBase64String(AesIV);// Encoding.UTF8.GetBytes(AesIV); 
                rijndaelCipher.Mode = CipherMode.CBC;
                rijndaelCipher.Padding = PaddingMode.PKCS7;
                ICryptoTransform transform = rijndaelCipher.CreateDecryptor();
                byte[] plainText = transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
                string result = Encoding.UTF8.GetString(plainText);
                return result;
            }
            catch (Exception)
            {
                return null;
            }
        }
        #endregion
        #region 通过客户端相关信息获取用户唯一标识(md5自定义后的)
        /// <summary>
        /// 通过客户端相关信息获取用户唯一标识(md5自定义后的)
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public ReturnModel<GetUserInfoOutputModel> GetUserData(GetUserInfoInputModel input)
        {
            string code = input.code;
            string iv = input.iv;
            string encryptedData = input.encryptedData;
            string Appid = CommonFun.GetConfigData("WeApp", "Appid");
            string Secret = CommonFun.GetConfigData("WeApp", "Secret");
            string grant_type = "authorization_code";
            //向微信服务端 使用登录凭证 code 获取 session_key 和 openid  
            string url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + Appid + "&secret=" + Secret + "&js_code=" + code + "&grant_type=" + grant_type;
            string type = "utf-8";
            string j = GetUrltoHtml(url, type);//获取微信服务器返回字符串 
            //将字符串转换为json格式 
            JObject jo = (JObject)JsonConvert.DeserializeObject(j);
            var res = new ReturnModel<GetUserInfoOutputModel>()
            {
                data = new GetUserInfoOutputModel()
            };
            try
            {
                //微信服务器验证成功 
                res.data.openid = jo["openid"].ToString();
                res.data.session_key = jo["session_key"].ToString();
                /*
                 skey:作为用户客户端标识,openid太重要不能暴露在客户端.
                 思路就是把skey,openid,用户昵称、头像等等信息存入数据库,skey在客户端、后端接口间流转作为标识,可以用来识别、存储、查询用户信息
                 */
                res.data.skey = EncryptHelper.MD5(res.data.openid);//作为用户客户端标识,openid太重要不能暴露在客户端
                res.data.skeyCheckCode = EncryptHelper.MD5(res.data.skey + res.data.ExpTime);//用来二次验证登录是否伪造
            }
            catch (Exception)
            {
                //微信服务器验证失败 
                res.errcode = jo["errcode"].ToString();
                res.errmsg = jo["errmsg"].ToString();
            }
            if (!string.IsNullOrEmpty(res.data.openid))
            {
                //用户数据解密 
                AesIV = iv;
                AesKey = res.data.session_key;
                string result = AESDecrypt(encryptedData);
                //存储用户数据 
                JObject _usrInfo = (JObject)JsonConvert.DeserializeObject(result);
                WeiXinUserInfo userInfo = new WeiXinUserInfo();
                userInfo.openId = _usrInfo["openId"].ToString();
                try //部分验证返回值中没有unionId,这里是小程序关联的微信公众号ID
                {
                    userInfo.unionId = _usrInfo["unionId"].ToString();
                }
                catch (Exception)
                {
                    userInfo.unionId = "unionId";
                }
                userInfo.nickName = _usrInfo["nickName"].ToString();
                userInfo.gender = _usrInfo["gender"].ToString();
                userInfo.city = _usrInfo["city"].ToString();
                userInfo.province = _usrInfo["province"].ToString();
                userInfo.country = _usrInfo["country"].ToString();
                userInfo.avatarUrl = _usrInfo["avatarUrl"].ToString();
                object watermark = _usrInfo["watermark"].ToString();
                object appid = _usrInfo["watermark"]["appid"].ToString();
                object timestamp = _usrInfo["watermark"]["timestamp"].ToString();
                res.data.nickName = userInfo.nickName;
                res.data.avatarUrl = userInfo.avatarUrl;
                Log4Logger.Info(new LogContent()
                {
                    Url = HttpContext.Current.Request.RawUrl,
                    LogIp = DataHelper.GetIP(),
                    LogDescription = "userInfo:" + JsonUtility.ToJson(userInfo) + "   res:" + JsonUtility.ToJson(res)
                });
                //可以执行存数据库操作
                //返回用户标识数据 
                return res;
            }
            return null;
        } 
        #endregion
    }
}

md5方法

        /// <summary>
        /// MD5 hash加密+自定义
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string MD5(string str, bool isCustomed = true)
        {
            if (str == null)
            {
                throw new ArgumentException("加密内容为NULL", "str");
            }
            str = str.Replace(" ", "");
            using (var md5Provider = new MD5CryptoServiceProvider())
            {
                var md5 = BitConverter.ToString(md5Provider.ComputeHash(Encoding.UTF8.GetBytes(str.Trim())));
                if (isCustomed)//自定义md5
                {
                    string left = "", right = "";
                    left += (char.IsDigit(md5, 6) ? md5.Substring(5, 1) : md5.Substring(7, 1));
                    left += (char.IsDigit(md5, 13) ? md5.Substring(12, 1) : md5.Substring(14, 1));
                    right += (char.IsDigit(md5, 20) ? md5.Substring(19, 1) : md5.Substring(21, 1));
                    right += (char.IsDigit(md5, 27) ? md5.Substring(26, 1) : md5.Substring(28, 1));
                    string middle = (char.IsDigit(md5, 14) ? md5.Substring(13, 1) : md5.Substring(15, 1));
                    md5 = (left + md5 + right).Insert(17, middle);
                }
                return md5;
            }
        }




2.3 读取配置文件

GetConfigData相关方法

        #region 文件 /Uload/Data.config
        private static string _DataConfigFile = string.Empty;
        /// <summary>
        /// 文件/Uload/Data.config
        /// </summary>
        public static string DataConfigFile
        {
            get
            {
                if (_DataConfigFile == string.Empty) _DataConfigFile = DataHelper.GetAbsolutePath("/Upload/Data.config");
                return _DataConfigFile;
            }
        }
        #endregion
        #region 通过相对路径获取文件夹绝对路径
        /// <summary>
        /// 通过相对路径获取文件夹绝对路径
        /// </summary>
        /// <param name="relativePath"></param>
        /// <returns></returns>
        public static string GetAbsolutePath(string relativePath)
        {
            string AbsolutePath = "";
            if (HttpContext.Current != null)
            {
                AbsolutePath = HttpContext.Current.Server.MapPath(relativePath);
            }
            else
            {
                //多线程执行这里
                relativePath = relativePath.Replace("/", "\\");
                if (relativePath.StartsWith("\\"))//确定 String 实例的开头是否与指定的字符串匹配。为下边的合并字符串做准备
                {
                    relativePath = relativePath.TrimStart('\\');//从此实例的开始位置移除数组中指定的一组字符的所有匹配项。为下边的合并字符串做准备
                }
                AbsolutePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, relativePath);
            }
            return AbsolutePath;
        }
        #endregion
        #region 获取/Upload/Data.config中的配置信息
        /// <summary>
        /// 获取/Upload/Data.config中的配置信息
        /// </summary>
        /// <param name="nodeName">Root下的节点名称</param>
        /// <param name="attrName">要获取的节点的属性名称
        /// 如果为""则获取节点的InnerText
        /// 否则获取节点的Attribute
        /// </param>
        /// <returns></returns>
        public static string GetConfigData(string nodeName, string attrName = "")
        {
            return GetConfigSettings(DataCache.DataConfigFile, nodeName, attrName);
        }
        #endregion
        #region 获取其他config文件中的节点信息
        /// <summary>
        /// 获取其他config文件中的节点信息
        /// </summary>
        /// <param name="path">文件路径</param>
        /// <param name="NodeName">Root下的节点名称</param>
        /// <param name="AttrName">要获取的节点的属性名称
        /// 如果为""则获取节点的InnerText
        /// 否则获取节点的Attribute
        /// <returns></returns>
        private static string GetConfigSettings(string path, string nodeName, string attrName = "")
        {
            string Ret = "";
            XmlDocument doc = null;
            object obj_doc = DataCache.Instance.Get<object>(DataCacheKey.DataConfigFile.ToString(), DataCache.DataConfigFile);//依赖文件的缓存机制,测试时,可以不要
            if (obj_doc == null)
            {
                if (File.Exists(path))
                {
                    doc = new XmlDocument();
                    doc.Load(path);
                    DataCache.Instance.Set<object>(DataCacheKey.DataConfigFile.ToString(), doc, DataCache.DataConfigFile);
                }
            }
            else
            {
                doc = (XmlDocument)obj_doc;
            }
            if (doc != null)
            {
                XmlElement Node = (XmlElement)doc.SelectSingleNode("/Root/" + nodeName);
                if (Node != null)
                {
                    if (attrName != "") Ret = Node.GetAttribute(attrName);
                    else Ret = Node.InnerText;
                }
            }
            return Ret;
        }
        #endregion


Upload/Data.config

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <Log4net ISTEXT="1"></Log4net>
  <WeApp Appid="wxd16eafbd0ebdXXXX" Secret="44718d44XXXX1ebdabc0b60844e8XXXX"></WeApp>
</Root>


3、效果图

    


登录日志





四:小结

这里实现的是自定义登录,并且登录有效期由自己管理,小程序官方还提供一个API,登录有效期由微信维护,具体可以看接口文档:

wx.checkSession(OBJECT):https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxchecksessionobject

通过上述接口获得的用户登录态拥有一定的时效性。用户越久未使用小程序,用户登录态越有可能失效。反之如果用户一直在使用小程序,则用户登录态一直保持有效。具体时效逻辑由微信维护,对开发者透明。开发者只需要调用wx.checkSession接口检测当前用户登录态是否有效。登录态过期后开发者可以再调用wx.login获取新的用户登录态。

可以使用自己自定义的方法结合官方过期机制,来处理登录有效期问题,要不要这样做主要是看个人需求。




版权声明:
作者:真爱无限 出处:http://www.pukuimin.top 本文为博主原创文章版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接.
« 上一篇下一篇 »

相关文章:

评论列表:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。