相信不少喜歡開(kāi)發(fā)的朋友都已經(jīng)知道微信小程序是個(gè)什么物種了,樓主也是從小程序內(nèi)測(cè)期間就開(kāi)始關(guān)注,并且也寫(xiě)過(guò)幾個(gè)已經(jīng)上線(xiàn)的微信小程序。但是基本上都是寫(xiě)的純前端,最近樓主從后端到前端寫(xiě)一個(gè)完整的小程序項(xiàng)目,中間碰到了一些問(wèn)題,樓主會(huì)找一些個(gè)人覺(jué)得有學(xué)習(xí)價(jià)值的點(diǎn)不定時(shí)的拿出來(lái)跟大家分享,希望對(duì)你有一些幫助。
????本次就從最基本的微信小程序登錄態(tài)維護(hù)開(kāi)始吧。小程序官方api文檔里面有對(duì)登錄態(tài)的一個(gè)完整的解釋?zhuān)⑶矣邢嚓P(guān)的代碼。想看詳情,可以出門(mén)右轉(zhuǎn):https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxloginobject我第一次看的時(shí)候沒(méi)怎么看懂,并且代碼沒(méi)有提供java版本的,這讓一個(gè)java程序員情何以堪,所以在努力研究了以后決定要做一個(gè)java版本的簡(jiǎn)單的demo放出來(lái)。
????作為服務(wù)端,如果想獲得到使用微信小程序的會(huì)員信息,就需要小程序作為客戶(hù)端把會(huì)員的基本信息傳過(guò)來(lái)。類(lèi)似于手機(jī)號(hào),openId可以作為當(dāng)前小程序中用戶(hù)的唯一性標(biāo)志。然而如果把會(huì)員的openId信息明文直接在服務(wù)端與小程序端來(lái)回傳輸?shù)脑?huà),會(huì)有安全性的問(wèn)題。萬(wàn)一被別人得到這個(gè)openId,就相當(dāng)于得到會(huì)員的手機(jī)號(hào)一樣,就可以做一些其他操作了,顯然是不安全的。
????為了解決這一問(wèn)題微信采用了相對(duì)安全的方式。
//app.js
App({
onLaunch: function() {
wx.login({
success: function(res) {
if (res.code) {
//發(fā)起網(wǎng)絡(luò)請(qǐng)求
wx.request({
url: 'https://test.com/onLogin',
data: {
code: res.code
}
})
} else {
console.log('獲取用戶(hù)登錄態(tài)失敗!' + res.errMsg)
}
}
});
}
})
微信小程序端會(huì)調(diào)用wx.login的api,然后會(huì)得到一個(gè)code,這個(gè)code對(duì)外人來(lái)講是沒(méi)有任何意義的,可以放心的傳給服務(wù)端。服務(wù)端得到code以后,加上你申請(qǐng)小程序時(shí)的appId, app secret,去調(diào)微信的接口
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code 就可以得到以下參數(shù):
openid 用戶(hù)唯一標(biāo)識(shí) session_key 會(huì)話(huà)密鑰 unionid 本字段在滿(mǎn)足一定條件的情況下才返回 其中openid 就是會(huì)員的唯一性標(biāo)記,此時(shí)服務(wù)端可以保存下來(lái)。 session_key 以后解密 unionId(整個(gè)開(kāi)放平臺(tái)會(huì)員的唯一性標(biāo)識(shí))時(shí)有用。
????服務(wù)端得到openid以后,為了后邊的交互,要保存下來(lái)。一般來(lái)講有兩種方式:一種是直接入數(shù)據(jù)庫(kù),一種是采用效率高一點(diǎn)的緩存。樓主采用的是后者,方式是redis。
????按照微信的建議此時(shí)需要生成一個(gè)不重復(fù)值作為openId的唯一性標(biāo)識(shí)。這里采用的是java的uuid。然后把這個(gè)uuid值作為key,把openid以及后面會(huì)用到的session_key作為value,存進(jìn)redis。并且把uuid值返回給小程序。這樣小程序就可以直接拿uuid值跟服務(wù)端交互。
????也許會(huì)有人問(wèn),如果有人得到uuid值其實(shí)跟得到openid沒(méi)什么區(qū)別啊,都相當(dāng)于是會(huì)員的唯一性標(biāo)志。
????所以這里要對(duì)這個(gè)uuid值進(jìn)行一個(gè)處理。首先存入redis時(shí)要有時(shí)效性。session_key在微信服務(wù)器有效期是30天,建議服務(wù)端緩存session_key不超過(guò)30天。當(dāng)小程序傳過(guò)來(lái)的uuid值過(guò)期時(shí),認(rèn)為這是過(guò)期的uuid,則重新走wx.login步驟。
????為了方便redis中不僅會(huì)寸uuid與openid的對(duì)應(yīng)關(guān)系。還會(huì)再存一條openid對(duì)應(yīng)uuid的記錄,目的是為了下一次重新wx.login步驟時(shí)根據(jù)openid找到之前老的uuid,如果存在的話(huà)就刪掉,然后查詢(xún)一條新的uuid值,并且把openid對(duì)應(yīng)的這條記錄也更新掉。這樣redis服務(wù)器中就不會(huì)有多余的臟數(shù)據(jù),減輕服務(wù)器的負(fù)擔(dān)。
????以上就是我理解的整個(gè)登錄態(tài)的過(guò)程,當(dāng)然還有wx.checkSession這些沒(méi)有講到,其實(shí)就是發(fā)現(xiàn)session_key失效是再重新走一遍上述的流程就可以了。所以沒(méi)有仔細(xì)說(shuō)。不知道我有沒(méi)有講清楚。我會(huì)把整個(gè)流程的關(guān)鍵代碼貼出來(lái),供大家參考。
@ActionKey("/loginByWeixin")
public void loginByWeixin() throws Exception {
logger.info("Start getSessionKey");
String json = HttpKit.readData(getRequest());
JSONObject reqJson = JSON.parseObject(json);
String jsCode = reqJson.getString("code");
if (jsCode == null || "".equals(jsCode)) {
logger.info("缺少必要參數(shù)");
renderJson(new OutRoot().setCode("100").setMsg(SYS.PARAMETER_FAIL));
} else {
List record = appInfoService.selectAppInfo();
String appId = record.get(0).get("app_id");
String appSecret = record.get(0).getStr("app_secret");
if (appId == null || "".equals(appId) || appSecret == null || "".equals(appSecret)) {
logger.info("缺少必要參數(shù)");
renderJson(new OutRoot().setCode("100").setMsg(SYS.PARAMETER_FAIL));
} else {
String url = "https://api.weixin.qq.com/sns/jscode2session";
String httpUrl = url + "?appid=" + appId + "&secret=" + appSecret + "&js_code=" + jsCode
+ "&grant_type=authorization_code";
String ret =