本文将详细的介绍微信小程序的登录流程以及在ssm框架下如何实现小程序用户登录(日后补上过滤器的配置。。。)
登录流程概要
主要的登录流程可以参考官方提供的一张流程图:
1.微信前台页面:
在微信版本更新之后,提高了安全机制,我们需要为用户提供一个授权按钮,让用户同意授权,页面中的button必须包含 open-type="getUserInfo"这个属性:
一般授权页面如下:
1. 同意当前小程序获取我的微信头像;
2. 同意当前小程序获取我的微信昵称等其他信息;
2.登录前台实现
当用户登录时,我们先去判断小程序缓存中用户的token存不存在:
-----如果存在,将token传到后台去检查redis缓存中的用户信息是否失效:
-----如果失效,前台将用户信息删除,重新调用登录接口。
-----未失效,返回原页面,后台更新缓存失效时间。
-----如果不存在,前台调用官方提供的小程序登录接口:wx.login(),获得它返回的code:
-----code获取成功,前台调用wx.getSetting()获取授权以及用户的授权配置:
-----用户授权成功,前台调用wx.getUserInfo()获取encryptedData(密文)和 iv(偏移向量)
------以上都获取成功后,我们将code,encryptedData和iv全部传到后台。
(code用来换取openid和sessionKey,encryptedData和iv用来获得并解密用户信息)
-----如果后台获取用户信息成功,返回token和用户信息到前台,前台小程序在缓存中更新token和用户信息。
登录成功。
-----------------------任意一个步骤失败,提示用户无法登录。
微信小程序前台登录函数如下(下面这个函数较长,可以拆分,看的更清晰):
login: function () {
let that = this;
let token = wx.getStorageSync("token");
console.log("进入页面登录开始取token"+token);
if (token) {
console.log("如果token存在");
wx.request({
url: urlMangerData.api0_check_token,///wxma/user/check-token
data: {
token: token
},
success: function (res) {
console.log("前台token存在,校验token后返回值"+JSON.stringify(res));
//如果后台缓存已经不在了
if (!res.data.success) {
console.log("如果后台缓存已经失效了,小程序清空token和userinfo和openid");
wx.removeStorageSync("token");
wx.removeStorageSync("userInfo");
that.login();
} else {
console.log("后台缓存中的信息也存在,直接返回");
// 回到原来的地方
wx.navigateBack();
}
}
});
}
console.log("如果token不存在");
//小程序登录,
wx.login({
success: function (res) {
if(res.code){
wx.getSetting({
success: res2 => {
if (res2.authSetting["scope.userInfo"]) {
wx.getUserInfo({
success:datas=>{
console.log("开始获取code和一些加密过的用户信息" + res.code);
console.log("datas.encryptedData" + datas.encryptedData);
wx.request({
url: urlMangerData.api0_loginAuth,
data: {
code: res.code,
encryptedData: datas.encryptedData,
iv: datas.iv
},
header: {
'content-type': 'application/json'
},
success: function (res) {
console.log("后台成功后得到返回值" + res.data.code);
if(res.data.code != 0) {
console.log("后台登录错误")
wx.hideLoading();
wx.showModal({
title: "提示",
content: "无法登录,请重试",
showCancel: false
});
return;
}
console.log("存储用户信息token" + res.data.token);
console.log("存储用户信息userInfo" + JSON.stringify(res.data.wxMaUserInfoExtends));
wx.setStorageSync("token", res.data.token);
wx.setStorageSync("userInfo", res.data.wxMaUserInfoExtends);
wx.setStorageSync("user_id", res.data.wxMaUserInfoExtends.user_id);
wx.navigateBack();
}
});//request
}
})//getuserinfo
}
}
})//getsetting
}
}//success
})
}
再贴一下第一次登录时,控制台打印信息:
3.登录后台实现
当用户登录时,先检查前台传来的token在redis中是否存在:存在即更新redis中用户信息的失效时间,不存在则返回登录失效信息。
前台获得失效信息后,正常去调用获取用户信息接口,传入code,encryptedData和iv:
controller层:
@RequestMapping(value = "/loginAuth", method = RequestMethod.GET, produces = "application/json; charset=utf-8")public String loginAuth(String code,String encryptedData,String iv) throwsWxErrorException {if(StringUtils.isBlank(code)) {return "empty jscode";
}//处理登录授权 获得openid和sessionkey,生成userid 并将session存到缓存中
WxMaAuthResult result =weixinMaService.dealLoginAuth(code,encryptedData,iv);returnJsonUtils.objectToJson(result);
}
service层:
/*** 处理小程序登录授权*/@OverridepublicWxMaAuthResult dealLoginAuth(String code,String data,String iv) {
WxMaJscode2SessionResult session=newWxMaJscode2SessionResult();//调微信官方接口获得sesssion_key openid存到 session对象里
try{
//code换取sessionKey
session=wxMaService.getUserService().getSessionInfo(code);this.logger.info(session.getSessionKey());this.logger.info(session.getOpenid());
}catch(WxErrorException e) {this.logger.error("获得sessionKey失败", e);
}
WxMaAuthResult wxMaAuthResult= newWxMaAuthResult();
//该类用来存储用户信息
WxMaUserInfoExtends wxMaUserInfoExtends= newWxMaUserInfoExtends();
WxMaUserInfo wxMaUserInfo= newWxMaUserInfo();//暂时写死失效时间
int expire=3600;
String sessionKey=session.getSessionKey();
String rawData=StringEscapeUtils.unescapeHtml4(data);//解密用户信息
try{
//sessionkey data iv 解密用户信息
wxMaUserInfo= this.wxMaService.getUserService().getUserInfo(sessionKey, data, iv);
}catch(Exception e) {//TODO Auto-generated catch block
logger.error("解密用户信息失败");
e.printStackTrace();
}
BeanUtils.copyProperties(wxMaUserInfo, wxMaUserInfoExtends);
String third_session=Base64UUID.ramdomID();
wxMaAuthResult.setToken(third_session);
String user_id=Base64UUID.ramdomID();
wxMaUserInfoExtends.setUser_id(user_id);//通过openid获取或新增用户信息
if(wxMaUserMapper.countAny(session.getOpenid())>0){//存在 数据库更新
wxMaUserMapper.addWxMaUser(wxMaUserInfoExtends);//这里是将用户信息存到redis
wxMaAuthSessionStorage.addWxMaSession(expire,third_session,wxMaUserInfoExtends);//不把openId传到前台
wxMaUserInfo.setOpenId("");
wxMaAuthResult.setIsReg(true);
wxMaAuthResult.setSuccess(true);
wxMaAuthResult.setWxMaUserInfoExtends(wxMaUserInfoExtends);
}else{//不存在 数据库保存信息
wxMaUserMapper.addWxMaUser(wxMaUserInfoExtends);
wxMaAuthSessionStorage.addWxMaSession(expire,third_session,wxMaUserInfoExtends);
wxMaUserInfo.setOpenId("");
wxMaAuthResult.setSuccess(true);
wxMaAuthResult.setIsReg(false);
wxMaAuthResult.setWxMaUserInfoExtends(wxMaUserInfoExtends);
} returnwxMaAuthResult;
}
实体类:
public classWxMaUserInfoExtends{privateString user_id;privateString openId;privateString nickName;privateString gender;privateString language;privateString city;privateString province;privateString country;privateString avatarUrl;privateString unionId;privateDate create_date;
...
}
public class WxMaAuthResult extendsWxMaJscode2SessionResult{privateString code;privateString token;private int expiresIn = -1;
Boolean isReg= false; //是否保存过用户信息
private booleansuccess;privateWxMaUserInfoExtends wxMaUserInfoExtends;
...
}
注:还有一些是api提供的。
redis存取方法:
@AutowiredprivateJedisClient jedisClient;/*** 存放token和用户信息*/
//内部事务回滚不影响外部事务
@Transactional(propagation =Propagation.REQUIRES_NEW)
@Overridepublic void addWxMaSession(intexpire,String token,WxMaUserInfoExtends wxMaUserInfoExtends) {String json=JsonUtils.objectToJson(wxMaUserInfoExtends);try{
jedisClient.set(token, json);
jedisClient.expire(token, expire);
}catch(Exception e) {
e.printStackTrace();
}
}
//从缓存中获取用户信息
@OverridepublicWxMaAuthResult getWxMaSessionBy3rdKey(String token) {
WxMaAuthResult wxMaAuthResult= newWxMaAuthResult();try{
String json=jedisClient.get(token);if(json == null) {
wxMaAuthResult.setSuccess(false);returnwxMaAuthResult;
}
wxMaAuthResult.setSuccess(true);
WxMaUserInfoExtends wxMaUserInfoExtends= JsonUtils.jsonToPojo(json, WxMaUserInfoExtends.class);
wxMaAuthResult.setWxMaUserInfoExtends(wxMaUserInfoExtends);returnwxMaAuthResult;
}catch(Exception e) {//TODO Auto-generated catch block
e.printStackTrace();
wxMaAuthResult.setSuccess(false);returnwxMaAuthResult;
}
}
注:这里使用了binarywang(一个github上的开源开发者)提供的API,该API封装了调用微信小程序官方接口的方法,并提供了一些小程序登录信息和用户信息的实体类,非常方便,有需要可以下载下来用:https://github.com/binarywang
后台大体流程:
1.我们用code去换取sessionKey和openId,再通过sessionKey,encryptedData和iv来解密用户信息(解密过程已经封装好了)。
2.生成一个随机字符串,将它作为key(token),用户信息作为value存入redis缓存并设置或更新失效时间。
3.返回登录结果和用户信息到前台。
原文出处:https://www.cnblogs.com/xjx199403/p/10785356.html