近期發現以前的系統中存在一個如下的Bug Case: 在Session過期時,執行頁面的ajax請求時,無法正常跳轉到session過期提示頁面,系統直接hold在那裡不動,只有點擊左側菜單時,系統才能跳轉到session過期提示頁面。 經過調研,發現是攔截器的處理問題,攔截器只攔截了Http請求,而沒有攔截Ajax請求,才導致出現上述Bug。 下面是解決辦法: 首先,優化攔截器: [java] /** * 攔截器 * @author lyh * @version 2013-11-1 * @see LoginFilter * @since */ public class LoginFilter implements Interceptor { /** * 序列號 */ private static final long serialVersionUID = -4979037503367919375L; /** * 日志 */ private static final Logger LOG = Logger.getLogger(LoginFilter.class); /** * ajax session超時返回值 */ private static String AJAX_TIME_OUT = null; /** * ajax 請求無權限返回值 */ private static String AJAX_NO_LIMIT = null; /** * ajax 請求異常返回值 (在每個ajax請求中處理) */ //private static String AJAX_EXCEPTION = null; /** * 放行url */ private static List<String> awayUrls = null; static { AJAX_TIME_OUT = "ajaxSessionTimeOut"; AJAX_NO_LIMIT = "ajaxNoLimit"; //AJAX_EXCEPTION = "ajaxException"; awayUrls = new LinkedList<String>(); //awayUrls.add("/login!userLogin.action"); //awayUrls.add("/custom!toLogin.action"); awayUrls.add("/equipment!upLoad.action"); } @Override public String intercept(ActionInvocation invocation) throws Exception { //獲取request域中信息 HttpServletRequest req = ServletActionContext.getRequest(); //獲得當前請求url String url = req.getServletPath(); //獲得請求類型 String type = req.getHeader("X-Requested-With"); //Object object = (Object)invocation.getAction(); //如果當前url在放行url集合內 則直接放行 if (!awayUrls.contains(url)) { UserInfoBean userinfo = (UserInfoBean)req.getSession().getAttribute( CommonConstant.AUTH_SESSION_USER_KEY); if (userinfo == null) { LOG.debug("用戶登錄會話已過期!"); //ajax請求 session過期時 返回字符串 if ("XMLHttpRequest".equalsIgnoreCase(type)) { PrintWriter printWriter = ServletActionContext.getResponse().getWriter(); printWriter.print(AJAX_TIME_OUT); printWriter.flush(); printWriter.close(); return null; } //普通http請求 直接返回頁面 else { return "sessionTimeOut"; } } else { //鑒權結果 boolean authFlag = false; try { //執行鑒權 authFlag = userManager_Service.isUrlInUserLimit(userinfo.getU_phone_num(), url); } catch (Exception e) { LOG.error(" 鑒權出現異常!異常信息:" + e.toString() + ":" + e.getMessage()); } //鑒權通過則放行 否則攔截 if (authFlag) { return invocation.invoke(); } //鑒權不通過 else { //ajax請求 session過期時 返回字符串 if ("XMLHttpRequest".equalsIgnoreCase(type)) { PrintWriter printWriter = ServletActionContext.getResponse().getWriter(); printWriter.print(AJAX_NO_LIMIT); printWriter.flush(); printWriter.close(); return null; } //其他Http請求 直接返回頁面 else { return "noLimit"; } } } } else { return invocation.invoke(); } } @Override public void destroy() { //do yourself } @Override public void init() { //do yourself } } 上述攔截器考慮了Ajax和Http兩種情況,Http請求被攔截時,直接跳轉到指定的全局頁面,而Ajax請求被攔截時則采用Js方式提示用戶。 [html] <package name="self-default" extends="json-default"> <interceptors> <interceptor name="loginFilter" class="xx.xx.LoginFilter" /> <interceptor-stack name="mydefault"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="tokenSession"> <param name="includeMethods">add*,update*,modify*,upload*</param> </interceptor-ref> <interceptor-ref name="loginFilter" /> </interceptor-stack> </interceptors> <!-- 攔截器應用到全部action --> <default-interceptor-ref name="mydefault"/> <global-results> <!-- 普通http請求時 系統出現異常返回到錯誤頁 --> <result name="exception">/file/smartmanager/public/globalException.jsp</result> <!-- 普通http請求時 無操作權限 --> <result name="noLimit">/file/smartmanager/public/noLimit.jsp</result> <!-- 普通http請求時 session過期 --> <result name="sessionTimeOut">/file/smartmanager/public/sessionTimeOut.jsp</result> </global-results> <global-exception-mappings> <!-- 全局異常返回exception字符串 --> <exception-mapping exception="java.lang.Exception" result="exception" /> </global-exception-mappings> </package> 下面是一個簡單的Action例子: [java] public class MyAction { /** * Http請求 * @return * @throws Exception * @see */ public String httpReqMethod() throws Exception { try { //do yourself } catch(Exception e) { //捕獲異常時拋出 觸發global-results中的exception 然後跳轉到提示頁面 throw e; } return "httpReqMethod"; } /** * Ajax請求 * @return * @throws Exception * @see */ public String ajaxReqMethod() { try { //do yourself } catch(Exception e) { //no throw //此處在捕獲異常時 添加提示信息至json 方便在頁面展示 //ajaxMap.put("success", false); //ajaxMap.put("opMsg", ResultMsg.CHANGE_PWD_ERROR); } return "ajaxReqMethod"; } } 配置此Action的xml(此Action需要被攔截 故extends定義的攔截器self-default): [html] <package name="willPackage" extends="self-default" namespace="/"> <action name="my_*" class="xx.xx.MyAction" method="{1}"> <result name="httpReqMethod">/file/smartmanager/main/httpReqMethod.jsp</result> <result name="ajaxReqMethod" type="json"> <param name="root">ajaxMap</param> </result> </action> </package> 全局提示頁面,globalException.jsp: [html] <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>xxx管理系統</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="icon" href="file/img/rhy.ico" type="image/x-icon" /> <link rel="shortcut icon" href="file/img/rhy.ico" type="image/x-icon" /> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/file/css/easyui.css" ></link> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/file/css/icon.css" ></link> <script type="text/javascript" src="${pageContext.request.contextPath}/file/scripts/jquery-1.8.3.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath}/file/scripts/jquery.easyui.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath}/file/scripts/easyui-lang-zh_CN.js"></script> <body> <script type="text/javascript"> $(function(){ $.messager.alert('操作提示','系統內部異常!請聯系系統管理員!','warning'); }); </script> </body> 全局ajax配置publicSetup.js: [javascript] //全局ajax控制,用於session超時 無權限時 提示 $.ajaxSetup({ cache: false, //close AJAX cache contentType:"application/x-www-form-urlencoded;charset=utf-8", complete:function(XHR,textStatus){ var resText = XHR.responseText; if(resText=='ajaxSessionTimeOut'){ sessionTimeOut(); } else if(resText=='ajaxNoLimit'){ noLimit(); } } }); function sessionTimeOut(){ $.messager.alert('操作提示','用戶登錄會話已過期,請重新登錄!','warning'); setTimeout('window.top.location.href = "login!exit.action"', 15); } function noLimit(){ $.messager.alert('操作提示','無相應操作權限,請聯系系統管理員!','warning'); } 在ajax請求頁面,引入上述的publicSetup.js,在ajax請求時會觸發上述js,如果session過期則會觸發全局提示。 [html] <%@ page contentType="text/html; charset=UTF-8" isELIgnored="false"%> <%@ taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta> <title>xx管理系統</title> <link rel="icon" href="${contextPath}/file/img/rhy.ico" type="image/x-icon" /> <link rel="shortcut icon" href="${contextPath}/file/img/rhy.ico" type="image/x-icon" /> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/file/css/easyui.css" ></link> <script type="text/javascript" src="${pageContext.request.contextPath}/file/scripts/jquery-1.8.3.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath}/file/scripts/jquery.easyui.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath}/file/scripts/easyui-lang-zh_CN.js"></script> <!-- 引入ajax配置 --> <script type="text/javascript" src="${pageContext.request.contextPath}/file/scripts/publicSetup.js"></script> <script type="text/javascript"> function ajaxPost() { $.post('my_ajaxReqMethod.action', params, function (data) { if (data.success) { } $.messager.alert('操作提示', data.opMsg, data.success?'info':'warning'); }); } </script> </head> <body> </body> </html> 綜上,在Http請求時,會攔截到session過期、無操作權限,然後跳轉到制定的全局提示頁面,全局的exception配置是通過在用戶自定義action方法中拋出Exception,然後觸發跳轉到全局提示頁面。 而Ajax請求時,攔截到session過期、無操作權限時,會通過全局設置的ajax.js進行控制,如果不滿足,則Alert提示語,ajax的Exception配置在ajax方法中捕獲,然後拼裝異常提示語至返回頁面Json,在頁面進行Exception提示。 二者的提示區別,Http請求攔截時跳轉到頁面進行提示,而Ajax請求攔截時采用的是Alert提示方式(當然你可以用EasyUI、ExtJS的Alert那樣更美觀)。