使用 Struts2 + Spring + Hibernate (SSH) + EasyUI,AJAX 請求返回 JSON。Session 過期時,後端拋異常或返回視圖,前端無法跳轉登錄頁(因非整頁請求,JS 無法處理重定向響應)。
public class SessionFilter implements Filter {
private static final String[] EXCLUDE_PATHS = {"/login", "/css/", "/js/", "/images/", "/error"};
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String uri = request.getRequestURI();
String ctx = request.getContextPath();
// 靜態資源、登錄頁放行
if (Arrays.stream(EXCLUDE_PATHS).anyMatch(p -> uri.startsWith(ctx + p))) {
chain.doFilter(request, response);
return;
}
HttpSession session = request.getSession(false);
boolean loggedIn = (session != null && session.getAttribute("user") != null);
// 判斷是否 AJAX 請求
boolean isAjax = "XMLHttpRequest".equals(request.getHeader("X-Requested-With"))
|| "application/json".equals(request.getHeader("Accept"));
if (!loggedIn) {
if (isAjax) {
// 返回 401 + JSON,前端統一攔截跳轉
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"msg\":\"Session expired, please login\"}");
} else {
// 整頁請求直接重定向
response.sendRedirect(ctx + "/login.jsp");
}
return;
}
chain.doFilter(request, response);
}
}
<filter>
<filter-name>sessionFilter</filter-name>
<filter-class>com.yourpkg.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Session 超時配置(分鐘) -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
$.ajaxSetup({
complete: function(xhr) {
if (xhr.status === 401) {
// 彈窗提示並跳轉
$.messager.alert('提示', '登錄已過期,請重新登錄', 'info', function() {
window.top.location.href = '${pageContext.request.contextPath}/login.jsp';
});
}
}
});
// EasyUI datagrid/form/ajax 統一錯誤處理
$(document).ajaxError(function(event, xhr) {
if (xhr.status === 401) { /* 同上 */ }
});
$.ajaxSetup 或 axios.interceptors 一處配置,全局生效。若不想強制跳轉,可在 Filter 中檢測 Refresh-Token Header,調用 Service 刷新 Access Token,返回新 Token 給前端(Header 或 Body),前端自動重試原請求。適合 App/SPA 長連接場景。