前言

這段時間給電影網站加了收費在線觀看的權限,由於之前的 APP 沒有添加登錄模塊,所以現在必須得添加上了。APP 基於 H5 MUI 開發,在實現的過程中真的是碰得焦頭爛額的。

過程

H5 開發 APP 無非就是利用 WebView 操作 html 代碼,寫個登錄頁面簡直就是順手拈來,但是!寫完登錄,發現登錄成功后再次發起其他的 ajax 請求,依舊是未登錄狀態,即 ajax 登錄后服務器 response 的 Set-Cookie 在 WebView 中不起作用。
既然如此,那么就在登陸后保存服務器返回的 JSESSIONID,后面的 ajax 請求添加上 cookie,無奈 mui.ajax headers 添加 cookie 不能成功!
到 DCloud 社區搜索相關問答,官方說 plus.navigator.setCookie() 可以設置 WebView 的 Cookie,於是去嘗試一下,發現沒點作用,幾經折騰,發現是離線打包的原因,在 Hbuilder 中用基座調試是沒問題的,沒辦法由於播放器部分必須用 Android 原生代碼開發,所以必須離線打包,MUI 的這個 H5 開發 APP 就是一坑,不想多說了,這個問題根本無從下手去找解決方法。
此時,代碼沒問題,是離線打包 SDK 的問題,頭都大了,心中一萬匹草泥馬奔騰起來,發誓再也不用 MUI 開發 APP 了。

解決方案

針對這種情況,解決辦法當然還是有的,只不過繁瑣一些罷了。
方案一:
將 H5 中的 ajax 交由 Android 原生代碼去做,這樣一來設置 Http 請求的 Cookie 就不可能會有問題了,實現需做的事情:編寫插件接口用 Java 代碼實現帶 cookie 的 http 請求,在 js 代碼中調用此接口。如果 SDK 離線打包沒問題的話,原本 plus.navigator.setCookie() 一句代碼就能解決的事變得這么繁瑣,不采用!
方案二:
對 Tomcat 服務器不熟悉的打破腦袋估計也想不到此方案。有點 Http 協議常識的人都知道,瀏覽器與服務器保持會話登錄狀態通常是利用 session 來實現的,當然了 cookie 也能,但是不安全。
實際上 session 的實現方案也可能需要借助 cookie(不是必需的),對於 Java Web,瀏覽器訪問服務器,都會產生一個 JSESSIONID,大多數情況下我們看到的是存在於 Cookie 中,對於早期的一些網站都是暴露在 URL 地址中的,正因如此,可能很多人就想不到此方案。
既然 WebView 中 ajax 設置 Cookie 不成功,那么能夠將 JSESSIONID 作為 http 請求參數傳給服務器后台嗎?
最初我的想法就是這樣的:

  1. ajax 登錄,SpringMVC Controller 中 session.getId() 獲取到 JSESSIONID 返回給客戶端。
  2. 接收登錄返回結果,將 JSESSIONID 通過 plus.storage.setItem() 存到本地。
  3. 進行需要權限的 ajax 請求時從本地獲取 JSESSIONID 並拼接到 url 中。

上面做法有個難點就是,后端獲取到 JSESSIONID 后如何通過 JSESSIONID 取得對應的 HttpSession 對象,經過一方搜索,幾乎沒有什么好的方案,唯一可行的就是通過實現 HttpSessionListener 接口,自己將所有的 session 都用一個 Map 保存起來,這樣一來就可以通過這個 Map 來根據 JSESSIONID 獲取對應的 session 對象了,但是,此方案不夠優雅,Tomcat 本身就存儲了所有的 session,這樣做顯得有點多此一舉,並且,session 的時效性又該如何控制?
到這里,內心是崩潰的。
不到黃河心不死,關鍵點在於 JSESSIONID,於是搜索 JSESSIONID,很多文章都是說如何去掉 url 中的 JSESSIONID,看到這里,雖然不是我要找的答案,但是正好相反,我要做的是如何把這個 JSESSIONID 不通過 cookie 傳給服務器,而是通過 url 傳給后台。
於是終於找到答案:
前面也說了一下,session 實現方案是可以借助 cookie 來傳遞 JSESSIONID 或者在 url 中來傳遞 JSESSIONID 的,只是我沒有想到,url 傳遞 JSESSIONID 不需要我們手動在后端來根據 JSESSIONID來獲取對應的 session 對象,服務器都給我們做好了的。
之所以會有 url 傳遞JSESSIONID這種方案,是因為當客戶端禁止了 cookie 后,為了能夠客戶端與服務器保持會話而做的。
到此,對於服務器的 session 又有了一個新的認識。
最終解決方案就是:
http 請求地址中是用如下方式攜帶JSESSIONID的:

http://xxx.com/action;jsessionid=xxxxxxxxxxxx

和普通的 http 參數不同,它是以分號開始追加到地址后面的(參數是以問號追加)。
這樣一來,服務器會自動地獲取 session_id,並找到對應的 HttpSession 對象,並不需要我們手動地去獲取(手動獲取在前面也分析過了,沒有什么好的實現方法)。所以,沒錯,就是這么簡單,WebView cookie 設置不成功有什么大不了,直接利用 url 傳遞 session_id 簡直方便快捷。
但是,由於之前 web.xml 配置出了點差錯,那是因為服務器 session 錯亂,找不到問題所在,就把 url 傳遞 session_id 給禁止了,也就是前面所說的很多文章關於去掉 url 中的 jsessionid 的,在 web.xml中加了下面配置:

  1. <session-config>
  2. <tracking-mode>COOKIE</tracking-mode>
  3. </session-config>

這簡直是坑爹,如此設置雖然能去掉 url 中的 jsessionid,是為了好看嗎?一旦客戶端禁止 cookie,需要登錄的會話操作就全部用不了了,現在想想,當時真的是病急亂投醫。
最后老老實實去掉該段配置,就能夠通過 url 的方式來傳遞 jsessionid 來保持登錄會話狀態了,問題得以解決。