問題產行於公司p2c交易平台的一個用戶充值模塊。
平台簡介:該平台通過第三方支付(以下使用“支付寶”代替)完成與各大銀行的交易(充值、投資、轉帳、提現等操作),並將數據保存在我司的服務器數據庫中,服務器使用tomcat,數據庫Mysql
平台充值流程:用戶登陸系統----點擊充值-----跳轉到支付寶----登陸支付寶------選擇銀行------充值完畢-----redrect到用戶中心(平台中的action為"preurl:/user/center"),問題來了,
當用戶充值完成后轉回用戶中心時平台轉回了登陸界面,需要用戶重新輸入一次用戶名和密碼才能到達用戶中心。
談到此種現象,但凡搞過java開發的軟件工程師應該都能想到是session問題,好吧,讓我們來一起研究一下從點擊充值到支付完成過程中session的變化。
1、用戶使用username 和 password登陸后會將用戶信息保存在session中,並且在客戶端瀏覽器的cookie中添加一個cookie(key:JSESSIONID,value:XXX);
2、用戶點擊充值按鈕跳轉到支付寶---XX銀行進行充值,由於XX銀行充值網站也是使用tomcat,那么cookie中默認的session key 也為 JSESSIONID,這樣以來充值完畢便修改了cookie中session的值;
3、平台使用spring security的用戶登陸校驗,攔截所有需要用戶登陸的界面,這樣以來,當充值完成返回preurl:/user/center的時候被spring攔截,並取出cookie中JSESSIONID所對應的值再去session里查找用戶信息;
4、由於XX銀行修改過的session與原session並非同一session,於是spring將其攔截到用戶登陸界面;
以上便是一個很常見的跨域訪問session失效問題,但是問題並非想象中那么簡單,至少筆者在此問題上費了很大的力氣,並尋問“眾大神”以及幾千人的技術群,無一人知曉(或許真正的大神不屑一顧吧),現解決辦法及說明總結如下:
1、總體思路
在用戶點擊充值時將sessionid保存下來,充值成功后再將sessionid取出來替換
2、具體實現
支付寶接口說明中有一個字段叫做merpri(私有域),官方解釋:“用戶自定義字段,可以將自己需要
的信息保存在里面,接口調用完成后原樣返回”,那么正好是我們需要的,在向支付寶write數據時,我將sessionid取到並放入merpri中,然后在回調時將merpri中的值取出,通過遍歷cookie數組將數組中名為JSESSIONID的cookie替換為之前的,關鍵代碼如下:
//說明:以下代碼為直接在博客中手寫如有錯誤請留言,以便及時更正
//從jsf中獲取
HttpServletRequest request = FacesUtil.getHttpServletRequest ();
HttpServletResponse response = FacesUtil.getHttpServletResponse();
//從支付寶返回參數中獲取之前保存的sessionid
String finalSession = request.getParameter("merpri");
Cookie[] cookies = request.getCookies();
for(Cookie c : cookies){
if(c.getName.equals("JSESSIONID")){
//如果是帶域名的網站需要配置,因為現在的問題是跨域session失效
c.setDomain(".alibaba.com");
//刪除cookie
c.setMaxAge(0);
//設置路徑(域名后面的主路徑)
c.setPath("/alibaba");
response.addCookie(c);
c.setDomain(".alibaba.com");
Cookie cookie = new Cookie("JSESSIONID",finalSession);
//設置生命周期為20分
cookie .setMaxAge(20);
cookie .setPath("/");
response.addCookie(cookie);
}
}
3、容易出問題的地方總結:
1) 不要將代碼放在耦合性較強的代碼邏輯中
2) cookie中sessionid的操作一定要在返回/user/center 之前最近位置(否則將無法刪除cookie)
3) cookie生命周期和路徑一定要注意匹配,應當使用調試工具查看cookie實時的一個值
4) 設置domain時需要特別注意,經本人測試以下四種瀏覽器:ie 9, firefox, 360安全, 獵豹
其中ie 9 和 firefox域為 .alibaba.com 而360安全,獵豹 的域為www.alibaba.com這樣就會出現:使用不同的瀏覽器設置cookie的域不同而導致其中2個瀏覽器依然無法正常跳轉到用戶中心,解決辦法:在代碼中判斷其內核,根據不同的內核設定不同的域。
5) 若使用的第三方支付沒有私有域的概念,那么請使用數據庫代替吧,即:將sessionid放入數據庫中,這樣以來要保證事務的完整性了
題外話:
(談到瀏覽器的兼容問題,IE瀏覽器不得不讓人大跌眼鏡,眾所知周,所謂“瀏覽器兼容”無非就是IE這個“笨重的家伙”,解析js速度慢到極點,並且效果看上去極差,這一點應該向谷歌和火狐學習),360有個新功能是這樣描述的:
我們新增加了一個控制手段:內核控制Meta標簽。只要你在自己的網站里增加一個Meta標簽,告訴360瀏覽器這個網址應該用哪個內核渲染,哪么360瀏覽器就會在讀取到這個標簽后,立即切換對應的內核。並將這個行為應用於這個二級域名下所有網址。
目前該功能已經在所有的360安全瀏覽器實現。我們也建議其它瀏覽器廠商一起支持這個實現。讓這個控制標簽成為行業標准。
在head標簽中添加一行代碼:
<html>
<head>
<meta name="renderer" content="webkit|ie-comp|ie-stand">
</head>
<body>
</body>
</html>
瀏覽器的兼容問題嚴重困擾了眾多的軟件工程師,不僅僅是前端開發,在此希望能夠出台一個瀏覽器標准將所有的瀏覽器內核規范處理,開發工程師使用這個規范去開發,以減少這種“由於瀏覽器間客戶流量競爭”而給開發者帶來的工作量。
