主要的登陸流程能夠參考官方提供的一張流程圖:git
在微信版本更新以後,提升了安全機制,咱們須要爲用戶提供一個受權按鈕,讓用戶贊成受權,頁面中的button必須包含 open-type="getUserInfo"這個屬性:github
通常受權頁面以下:redis
<form bindsubmit="bindSave"> <wxc-button type="success" size='large' btn-style='margin-top:10%;' open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo" value="贊成受權登陸"></wxc-button> <view style='width:100%;padding-left:30rpx;font-size: 30rpx;margin-top:50rpx;'>1. 贊成當前小程序獲取個人微信頭像;</view> <view style='width:100%;padding-left:30rpx;font-size: 30rpx;margin-top:20rpx;'>2. 贊成當前小程序獲取個人微信暱稱等其餘信息;</view> <wxc-button type="danger" size='large' btn-style='margin-top:30rpx;' bindtap='rejectLogin' value="拒絕受權"></wxc-button> </form>
當用戶登陸時,咱們先去判斷小程序緩存中用戶的token存不存在:數據庫
-----若是存在,將token傳到後臺去檢查redis緩存中的用戶信息是否失效:json
-----若是失效,前臺將用戶信息刪除,從新調用登陸接口。小程序
-----未失效,返回原頁面,後臺更新緩存失效時間。微信小程序
-----若是不存在,前臺調用官方提供的小程序登陸接口:wx.login(),得到它返回的code:api
-----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
})
}
再貼一下第一次登陸時,控制檯打印信息:
當用戶登陸時,先檢查前臺傳來的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) throws WxErrorException { if (StringUtils.isBlank(code)) { return "empty jscode"; } //處理登陸受權 得到openid和sessionkey,生成userid 並將session存到緩存中
WxMaAuthResult result = weixinMaService.dealLoginAuth(code,encryptedData,iv); return JsonUtils.objectToJson(result); }
service層:
/** * 處理小程序登陸受權 */ @Override public WxMaAuthResult dealLoginAuth(String code,String data,String iv) { WxMaJscode2SessionResult session =new WxMaJscode2SessionResult(); //調微信官方接口得到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 = new WxMaAuthResult();
//該類用來存儲用戶信息 WxMaUserInfoExtends wxMaUserInfoExtends = new WxMaUserInfoExtends(); WxMaUserInfo wxMaUserInfo = new WxMaUserInfo(); //暫時寫死失效時間
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); } return wxMaAuthResult; }
實體類:
public class WxMaUserInfoExtends{ private String user_id; private String openId; private String nickName; private String gender; private String language; private String city; private String province; private String country; private String avatarUrl; private String unionId; private Date create_date; ... }
public class WxMaAuthResult extends WxMaJscode2SessionResult{ private String code; private String token; private int expiresIn = -1; Boolean isReg = false; //是否保存過用戶信息 private boolean success; private WxMaUserInfoExtends wxMaUserInfoExtends; ... }
注:還有一些是api提供的。
redis存取方法:
@Autowired private JedisClient jedisClient; /** * 存放token和用戶信息 */ //內部事務回滾不影響外部事務 @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void addWxMaSession(int expire,String token,WxMaUserInfoExtends wxMaUserInfoExtends) { String json = JsonUtils.objectToJson(wxMaUserInfoExtends); try { jedisClient.set(token, json); jedisClient.expire(token, expire); } catch (Exception e) { e.printStackTrace(); } } //從緩存中獲取用戶信息 @Override public WxMaAuthResult getWxMaSessionBy3rdKey(String token) { WxMaAuthResult wxMaAuthResult = new WxMaAuthResult(); try { String json = jedisClient.get(token); if(json == null) { wxMaAuthResult.setSuccess(false); return wxMaAuthResult; } wxMaAuthResult.setSuccess(true); WxMaUserInfoExtends wxMaUserInfoExtends= JsonUtils.jsonToPojo(json, WxMaUserInfoExtends.class); wxMaAuthResult.setWxMaUserInfoExtends(wxMaUserInfoExtends); return wxMaAuthResult; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); wxMaAuthResult.setSuccess(false); return wxMaAuthResult; } }
注:這裏使用了binarywang(一個github上的開源開發者)提供的API,該API封裝了調用微信小程序官方接口的方法,並提供了一些小程序登陸信息和用戶信息的實體類,很是方便,有須要能夠下載下來用:https://github.com/binarywang
後臺大致流程:
1.咱們用code去換取sessionKey和openId,再經過sessionKey,encryptedData和iv來解密用戶信息(解密過程已經封裝好了)。
2.生成一個隨機字符串,將它做爲key(token),用戶信息做爲value存入redis緩存並設置或更新失效時間。
3.返回登陸結果和用戶信息到前臺。