小程式登入態維持
這幾天一直在搞小程式,對於其登入態的維持和後臺執行機制一直有些困惑。
花了很多時間完成了,這裡可以給新手指條路,也給自己做個筆記。
分為下面幾個方面來講
- 常規Web專案登入態維持
- 小程式的特殊點
- 如何在小程式中實現登入態的維持
一、常規Web專案登入態維持
這裡就文字簡述一下,關於Web專案登入態維持網上資料實在是很多,不累贅了。
1.使用者開啟頁面——服務端接受請求,給予頁面,並給其建立一個會話(通過sessionID來標誌這個客戶端,sessionID儲存在服務端)。
F12就可以看到,你在同一個網站登入後,所有請求的Cookie值都不會變。
2.在後臺我們開發的時候,在登入時會在session中儲存當前使用者的姓名,使用者ID等,在程式開時可以直接從session中取得使用;為什麼能從session取得當前使用者的ID,而不會和其他使用者混淆呢?
二、小程式的特殊點//可以看到session從request中取得,然而request中就包含了請求的所有資訊,包括sessionID來認識你的當前會話 //只要你的sessionID與伺服器中快取的sessionID相同,服務端就認識你,你就能取得那一塊的session快取資料。 //這樣就維持了針對客戶端的會話概念。 HttpServletRequest request = ServletActionContext.getRequest(); HttpSession session=request.getSession();//獲取session session.setAttribute(arg0, arg1);//往session裡儲存鍵值對 session.getAttribute(name);//根據key取得value String sessionID=session.getId();//從客戶端取得sessionID,sessionID在客戶端儲存名為JSESSIONID,一般web專案,一次會話的sessionID不變,而小程式每次請求都會變。。。。所以小程式並沒有會話的概念
新手需要先去看看小程式的簡易教程,然後下個開發者工具,看一下預設的專案結構。
1.小程式前臺與後臺開發分離,但是不存在跨域問題,因為一次請求的順序是這樣的:小程式 —>微信服務端 —>第三方服務端(也就是你的後臺)—>微信服務端—>小程式。
2.如上也就意味著小程式中不存在會話的概念,無法在session中儲存使用者ID等資訊,只能通過自己的手段解決。同時,小程式與微信服務端也會維持登入態。
3.那麼暴露的後臺如何保證登入與後續身份驗證的安全性呢?登入:使用code請求openid驗證使用者身份;後續身份驗證:登入後將你自己的sessionID用自己的邏輯實現,維持小程式與第三方服務端的登入態,而sessionID儲存在小程式中。
4.我們來看看,其他開發小程式的同志的困難吧,同時也佐證上幾條。
(官方邏輯圖)
(官方論壇提問)
(官方回答)
所以,其實官方論壇裡的邏輯圖是不對的,無法儲存在session中,有人也許會說儲存在ServletContext,可是這不常規,影響效能,且無法定時清除。
下面是一些帖子,還有很多其他看過的帖子,一個意思,我沒儲存就不貼了。
三、小程式如何維護登入態
這裡是我綜合大部分邏輯和觀點的登入態維護程式碼,已經實現,從頭開始一步一步把程式碼貼出來。
1.之前說過,小程式是與微信後臺服務對接的,他們之間也有登入態,如下wx.login()就是小程式與微信後臺登入,從login中能獲取code,發到第三方後臺,獲取你自己的登入態。
//使用者登入公共方法
userLogin:function(){
var me=this;
// 登入
wx.login({
success: res => {
// 傳送 res.code 到後臺換取 openId, sessionKey, unionId
var errMsg = res.errMsg;
if (errMsg != "login:ok") {
me.showHint("錯誤提示","出錯了,請稍後再試試...")
} else {
var code = res.code; //(獲取code程式碼)
wx.request({
url: 'http://localhost:8080/wxCode/sys_login', //僅為示例,並非真實的介面地址
data: {
code: code
},
header: {
'content-type': 'application/json' // 預設值
},
success: function (res) {//code去後臺換取openid,且將uuid(sessionID儲存到前臺),最好儲存在localstrage,但是官方獲取localstrage方法有時候會出錯,就儲存到app全域性變數中了。
var data = JSON.parse(res.data);
me.globalData.name = data.name;
me.globalData.Cookie = data.uuid;
me.globalData.openid = data.openID;
if (data.hint == "NOUser") {
} else {
wx.redirectTo({
url: '../sysPage/choseFunc',
})
me.showHint("登入提示", data.successMark > 0 ? "登入成功!" : "登入失敗!請重新登入!");
}
}
})
}
}
})
},
2.後臺獲取openid,這裡就是請求地址,獲取openid,因為code唯一,一次性,只要能從code獲取openid就能保證是從小程式登入的正確使用者(請求用https,官方已經申明不支援http請求了)
//傳送推送請求
public static JSONObject sendGet(String URLParam) {
JSONObject backJSON=null;
try {
final String ADD_URL = URLParam;
//建立連線
URL url = new URL(ADD_URL);//建立java url物件
HttpsURLConnection connection = (HttpsURLConnection) url
.openConnection();//建立該物件的https請求
connection.setDoOutput(true);//設定請求允許向對方伺服器傳輸
connection.setDoInput(true);//設定請求允許己方接收傳輸
connection.setRequestMethod("POST");//設定傳輸方式POST
connection.setUseCaches(false);//POST請求不允許設定快取
connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
connection.connect();//建立連結
//POST請求
DataOutputStream out = new DataOutputStream(
connection.getOutputStream());
// out.write(obj.toString().getBytes("UTF-8"));//傳輸中文需要標明轉碼格式
out.flush();
out.close();
//讀取響應
BufferedReader reader = new BufferedReader(new InputStreamReader(
connection.getInputStream()));//接收流
String lines;
StringBuffer sb = new StringBuffer("");
while ((lines = reader.readLine()) != null) {
lines = new String(lines.getBytes(), "ASCII");
sb.append(lines);
}//讀取返回的資料
reader.close();
// 斷開連線
connection.disconnect();
System.out.print(sb);
backJSON=JSONObject.parseObject(sb.toString());
// String back=sb.toString();
// int x=back.indexOf(":");
// int y=back.indexOf(",");
// errcode=back.substring(x+1,y);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return backJSON;
}
//獲取微信使用者資訊
public static JSONObject getWXUserInfo(String code) {
//ruanxing小程式
String appid = "AAAAAAAAAAAAAAAAAAAA";
String secret = "BBBBBBBBBBBBBBBBBBBBBBBBBBBB";
String URLParam = "https://api.weixin.qq.com/sns/jscode2session?appid="+appid+""
+ "&secret="+secret+"&js_code="+code;
//傳送 GET 請求
//System.out.println(authURL);
JSONObject backJSON = wxFunc.sendGet(URLParam);
return backJSON;
}
3.後臺我就不貼了,你們可以根據自己的業務邏輯識別openid,且將uuid(sessionID)儲存在伺服器中,無論你是啥形式都可以,我是儲存在資料庫中的,因為沒法用session啊啊啊啊,媽的智障騰訊。(這裡還有一點,如果是要實現自己的業務邏輯,就需要一個openid繫結業務賬號的過程,這個自己解決吧)
4.封裝wx.request(),並將uuid帶入header頭部資訊;
//封裝小程式ajax請求。
/**
* header自動cookie
* 自動判斷是否登入超時,退出登入邏輯
* 自動轉化data為JSON物件
* **/
sendRequest:function(json){
var me=this;
wx.request({
url: json.url,
data: json.data,
method: 'GET',
header: {
'content-type': 'application/json', // 預設值,
'Cookie': "uuid=" + me.globalData.Cookie //這個方法放在app.js中,作為全域性方法,且自動將Cookie帶入。
},
success: function (res) {//封裝登入超時或錯誤自動退出邏輯
var data=JSON.parse(res.data);
if (data.ifSuccess =="NeedLogin"){
wx.redirectTo({
url: '../index/index',
})
me.showHint("登入提示", "登入超時,請關閉小程式重新登入!");
}else{
json.success(data);
}
},
fail: function (res) {
var data = JSON.parse(res.data);
json.fail(data);
//console.log(".....fail.....");
}
})
},
//使用方法
app.sendRequest({ url:"http://localhost:8080/wxCode/business_getDataOfSKD", data:{
}, success:function(data){ console.log(data); } });
5.後臺獲取wx.request中的hearder的cookie資訊。
HttpServletRequest request = ServletActionContext.getRequest();
Cookie[] cookies = request.getCookies();//這樣便可以獲取一個cookie陣列
for(Cookie cookie : cookies){
if(cookie.getName().equals("uuid")){
uuid=cookie.getValue();
}
}
//實現自己的業務邏輯............
四、總結1.小程式是前後臺分離,微信伺服器訪問第三方伺服器。
2.每個技術BAT企業都會犯錯,官方文件概念模糊,錯亂,再所難免。
3.多看API多看論壇,多理思路,但API不可全信,還是網友思路靠譜。
如有錯誤和不對的地方,麻煩指正哦