1. 程式人生 > >小程式登入態維持

小程式登入態維持

這幾天一直在搞小程式,對於其登入態的維持和後臺執行機制一直有些困惑。

花了很多時間完成了,這裡可以給新手指條路,也給自己做個筆記。

分為下面幾個方面來講

  • 常規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不可全信,還是網友思路靠譜。

如有錯誤和不對的地方,麻煩指正哦