作者:Mr.Voop,来自公众号:宏记 小程序官方文档中说道: 调用wx.login接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话密钥(session_key)。调用wx.getUserInfo接口将返回的数据会包含 encryptedData, iv 等敏感信息。用户数据的加解密通讯需要依赖会话密钥完成。 UnionID机制说明: 如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。 小程序登录主要作用是获取用户信息,主要用到两个API,一个是wx.login获取用户的jscode,再通过服务器端用jscode去获取用户的openid与session_key。openid是用户在小程序公众平台的唯一标识,注意,是小程序公众平台内,而不是所有公众平台内,即如果在服务号或订阅号公众平台,就可能会有跟此用户同样openid的用户,如果要微信所有公众平台某个用户的唯一标识,就只能用unionid了。session_key是用户的密钥,用于解密用户敏感数据。另一个wx.getuserInfo就是为了获取到用户的加密敏感数据与偏移量iv,偏移量再加上之前的session_key就能解密用户的敏感数据了。 官方提供的登录流程图:
下面是主要步骤流程: - 客户端调用wx.login获取code
- 客户端调用wx.request请求把code传到服务器后台
- 服务器后台根据code+appid+appsecret向微信后台发送请求获取openid+session_key
- 服务器后台随机生成一个随机数3rd_session(以硬盘或设置或日期+rand随机函数生成),以3rd_session为key,openid+session_key为Value存储起来,并把3rd_session发送至客户端,3rd_session所起的作用只是为了验证用户登录时的时效是否过期,并不是必须的,如不用验证登录是否过期,可不用3rd_session
- 客户端获取到3rd_session后存储到storage
- 客户端调用wx.getUserInfo获取用户敏感数据encryptedData和偏移向量IV
- 客户端调用wx.request请求把encryptedData+偏移向量iv+3rd_session传送到服务器后台
- 服务器后台根据3rd_session到缓存中找出session_key
- 服务器后台根据session_key+IV解密encryptedData(AES解密三个参数:密文encryptedData,密钥session_key,偏移向量iv)
步骤流程结合的详细流程图:
小程序客户端发起的调用: wx.login({ success: function (res) { var jscode = res.code; wx.getUserInfo({ success: function (res) { var option = { url: config.ghost+config.UrlApi.app_userlogin_url, method:'POST', data: { "obj":{ "encryptedData": res.encryptedData, "iv": res.iv, "jscode": jscode }, "UserCode": "", "EncryptUserCode": config.gencryptCusCode, "AppType":config.gapptype, "CompanyKey":config.gcompanyKey, "ShopID":config.gshopID } }; utils.ClientRequest(option, function (res) { console.log(res); if(res.data.Body.Code=="1"){ //成功 var resData = res.data.Body.Data; //初始化 that.setConfigUser(resData); //把Clientkey存缓存 storage.setStorageData(config.storageKey.Userkey,resData.clientkey); } else{ console.log(res); wx.navigateTo({url: "../loginfail/loginfail"}); } }, function (res) { console.log(res); wx.navigateTo({url: "../loginfail/loginfail"}); } ); }, fail: function () { // 拒绝操作 wx.navigateTo({url: "../loginfail/loginfail"}); } }); } });
后台业务服务端如下: public SingleResult<object> GetWXLoginInfo(UserInfoWhere Model, MolaResult molaResult) { var result = new SingleResult<object>(); using (var db = DbServices.GetDb()) { try { //获取商户信息 BIShopEntity shopinfo = db.GetDal<BIShopEntity>().Get(w => w.ShopCode == molaResult.ShopID); //1.使用登录凭证WModel.code 获取 session_key 和 openid var unifiedorderRes = WXHandleBll.GetSessionKeyOpenId(shopinfo.AppId, shopinfo.Secret, Model.jscode); //反序列化 var reslogion = JsonConvert.DeserializeObject<WXResLoginInfo>(unifiedorderRes);
if (reslogion.session_key != null) { //解密敏感数据 openid在每个平台是唯一的,不变的,而unionid是所有平台唯一的,而session_key是一段时期内相同,但过段时间有可能会变 var userdata = WXHandleBll.GetDencryptedData(Model.encryptedData, reslogion.session_key, Model.iv); //反序列化userdata成WXResencryptedData实体 var encryptedData = JsonConvert.DeserializeObject<WXResencryptedData>(userdata);
//随机数,返回客户端 key-value 暂不用随机,客户端用openid,这样删除应用再登录进来时才能识别是否注册过了 var clientKey = WXHandleBll.GetNoncestr();
//存用户表 先找,没有就增加,有就不管了 BIUserLoginEntity usermodel = db.GetDal<BIUserLoginEntity>().GetList(a => a.OpenID == encryptedData.openId).FirstOrDefault(); BIUserEntity user = new BIUserEntity(); BIUserLoginEntity userlogin = new BIUserLoginEntity(); if (usermodel == null) { user.UserCode = UtilService.GetBillCode(db, "BI_User"); user.UserName = encryptedData.nickName; user.RegisterTime = DateTime.Now; user.Createtime = DateTime.Now; user.Province = encryptedData.province; user.City = encryptedData.city; user.Sex = encryptedData.gender; user.HeadImage = encryptedData.avatarUrl;//头像 db.GetDal<BIUserEntity>().AddEx(user);
user.EncryptUserCode = encryptedData.openId;
//这样,第三方的登录态是还没做起来的,暂不做 userlogin.UserCode = user.UserCode; userlogin.JsCode = Model.jscode; userlogin.Sessionkey = reslogion.session_key; userlogin.OpenID = reslogion.openid; userlogin.Clientkey = encryptedData.openId;// clientKey; userlogin.CreateTime = DateTime.Now; //存登录信息 db.GetDal<BIUserLoginEntity>().AddEx(userlogin);
Model.UserCode = user.UserCode;
} else { user = db.GetDal<BIUserEntity>().GetList(a => a.UserCode == usermodel.UserCode).FirstOrDefault();
user.EncryptUserCode = encryptedData.openId;
}
WXResLoginInfo reslogin = new WXResLoginInfo(); reslogin.user = user; reslogin.shop = shopinfo; reslogin.clientkey = encryptedData.openId;// clientKey; result.Data = reslogin; result.Code = ((int)CommonStatus.Success).ToString(); result.Message = "获取成功!"; } else { result.Code = ((int)CommonStatus.Fail).ToString(); result.BillCode = "";//返回key result.Message = "获取失败!"; }
}
catch (Exception ex) { result.Code = ((int)CommonStatus.Abnormal).ToString(); result.Message = "登录获取失败!"; throw ex; } } return result; }
返回敏感数据格式如下: “{\”openId\”:\”o7uAX0bcV-vUiWnSx7L2E\”,\”nickName\”:\”翔\”,\”gender\”:1,\”language\”:\”zh_CN\”,\”city\”:\”Guangzhou\”,\”province\”:\”Guangdong\”,\”country\”:\”CN\”,\”avatarUrl\”:\”http://wx.qlogo.cn/mmopen/vi_32/DwVhddtjryWwk6wMVKp2RZq4vxS0FtQIOz6WqDONz3BkWJb0SE4a5ibf8Hibg7STFiaaicibKCQax0XkA/0\",\"watermark\":{\"timestamp\":149251873,\"appid\":\"wx11cb7c92\"}}“
|