在做 Web Application 時,因為 Web Project 有 session 自動失效的問題,所以如何讓用戶登錄一次系統就能長時間運行三個月,就是個問題。
后來,看到 session 失效的攔截器代碼,就猜想能否通過攔截器來實現。
查資料發現可行:用戶登錄時將帳號密碼存入cookie,cookie可以存儲1年至更久,當session失效被攔截時,在攔截器內讀取cookie 中的用戶名和密碼后再登錄。(登錄過程隱藏對用戶不可見)。
在實踐過程中遇到個問題: 在攔截器內重新實現登錄過程(包括寫session),此時寫session是成功的,但是返回被攔截器攔截的 Action 方法內時,Action 內的方法讀取的session卻是空的。(2017-06-12 更新,出錯原因是保存的sesion和讀取的session分別是Strut2包裝的session和JSP/Sevlert內置的session,導致讀取為空,參見)
方法一:設置 session-timeout 參數值
最簡單的辦法是,在Java Web 中可通過設置 web.xml 中的 session-timeout 為 -1 即可實現 session 永不失效。
Java Web中有關 Session 失效的設置有三處:
1. 頁面或者代碼內通過 session.setMaxInactiveInterval
2. 項目的web.xml 中的 session-timeout?(驗證有效)
3. Tomcat 的 server.xml 中的 ?(該方法本文未驗證,網上搜集)
defaultSessionTimeOut="3600" isWARExpanded="true"
isWARValidated="false" isInvokerEnabled="true"
isWorkDirPersistent="false"/>
4. Tomcat 的 web.xml 中的?(該方法本文未驗證,網上搜集)
30
分別對應:
1. 當前會話生效
2. 整個Web應用有效
3. 不詳
4. 不詳
設置產生效果的優先順序很顯然是:1 -> 2 ,沒有指明就是用默認設置。
另外:?setMaxInactiveInterval的參數是秒,session-config當中配置的session-timeout是分鐘。 ?設置session-timeout是永久有效。【setMaxInactiveInterval(-1)也是元永久有效(本人未測試)】。測試是否是永久有效很簡單,只需要在本地測試(localhost)時,將本機的時間調整為幾個月之后即可。
但在實際使用中并不理想,因為超出 tomcat 的默認是 30 分鐘,如果修改 tomcat 默認值,會影響其他項目不說,也很容易遺忘,對今后產生莫名其妙的問題。
方法二:使用 Struts2 攔截器
原理:在使用了 Struts 框架的代碼中,使用攔截器,測試Session 是否為空,若是空,利用 cookie 里藏的用戶名和密碼進行登錄,并保存到 session 中。
代碼:(本人親測,實際使用)
1. 登錄保存到 Struts2 包裝的 session中(注意,前后session要對應)
根據項目需要,可決定是否加密。我示例未加密。
//你的登錄方法內部//user 是登錄成功后的用戶對象
ActionContext.getContext().getSession().put("user", user);//創建或覆蓋COOKIE
Cookie ckName = new Cookie("jsjgUserName", username);
Cookie ckPwd= new Cookie("jsjgUserPwd", password);
ckName.setMaxAge(365*24*60*60);
ckPwd.setMaxAge(365*24*60*60);
response.addCookie(ckName);
response.addCookie(ckPwd);
2. 攔截器代碼
packagecom.tools;importjava.util.Map;importjavax.servlet.ServletContext;importjavax.servlet.http.Cookie;importjavax.servlet.http.HttpSession;importorg.apache.struts2.ServletActionContext;importorg.apache.struts2.StrutsStatics;importorg.springframework.context.ApplicationContext;importorg.springframework.web.context.support.WebApplicationContextUtils;importcom.opensymphony.xwork2.ActionInvocation;importcom.opensymphony.xwork2.interceptor.Interceptor;importcom.zhzx.class_entity.User;importcom.zhzx.service.UserService;
@SuppressWarnings("serial")public class MyInterceptor implementsInterceptor {public voiddestroy() {
}public voidinit() {
}public String intercept(ActionInvocation actionInvocation) throwsException {//確認Session是否過期
Map strutSession =actionInvocation.getInvocationContext().getSession();
Cookie[] cookies=ServletActionContext.getRequest().getCookies();
User us=(User) strutSession.get("user");
String userName= "";
String userPwd= "";if (us!=null&&!"".equals(us)) {returnactionInvocation.invoke();
}else{//從cookie得到登錄賬戶
if(cookies != null){for(Cookie cookie:cookies){if(cookie.getName().equalsIgnoreCase("jsjgUserName")){
userName=cookie.getValue();
System.out.println(userName);
}else if(cookie.getName().equalsIgnoreCase("jsjgUserPwd")){
userPwd=cookie.getValue();
}
}//登錄
if(userName != null && userPwd != null){//因項目使用了SSH,所以這里是獲取Spring管理的bean
ServletContext context =(ServletContext) actionInvocation.getInvocationContext().get(StrutsStatics.SERVLET_CONTEXT);
ApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(context);
UserService userService= (UserService)ctx.getBean("UserService");//是否注入成功
System.out.println(userService);//登錄并放入Strtu2包裝的session
User user=userService.Login(userName);if(user != null &&user.getPassword().toLowerCase().equals(userPwd.toLowerCase())){
strutSession.put("user", user);
}returnactionInvocation.invoke();
}
}return "timeout";
}
}
}
然后攔截器按照常規方法使用即可。攔截器可以看看?Struts in Action ,Struts實戰之類的書及PDF,有詳細的講解和例子。
吐槽:Java是太龐雜了,框架多不說,使用起來也方法各樣,一會兒流行配置文件,一會兒流行注解,一會SSH,一會SSM,一會Sping MVC。
我就想說這不都是自己折騰自己么,學習成本不是成本么,發明出這么多輪子,軟件行業不還是加班。舊的軟件項目不還是要維護。
Java體系真是自己折騰自己,真心無愛了。
我覺得框架只是工具,就像武俠世界里,真正的高手是用什么工具都可以殺人于無形,那管你是用很多人鄙視的 ASP.NET 還是自以為很牛逼的Java Web,
甲方和老板關心的無非是你做完了么,做的好看么,是不是?
程序員,工程師們還是消停點好,多關心下自己的健康和身邊的人不好么?