本文是《輕量級 Java Web 框架架構設計》的系列博文。 前端
今天想和你們簡單的分享一下,在 Smart 中是如何作到訪問安全控制的。也就是說,當沒有登陸或 Session 過時時所作的操做,會自動退回到首頁(例如:登陸頁面),以防止用戶進行非法操做。 java
這件事情或許是每一個具有安全性考慮的系統都須要的功能,咱們能夠分兩種狀況來處理用戶的請求: ajax
下面是具體的實現過程,您別忘了繫好安全帶,咱們這就出發了! 安全
第一步:在 Smart Framework 中定義一個認證異常類 AuthException 架構
public class AuthException extends RuntimeException { public AuthException() { super(); } public AuthException(String message) { super(message); } public AuthException(String message, Throwable cause) { super(message, cause); } public AuthException(Throwable cause) { super(cause); } }
沒啥內容,就一個普通的 RuntimeException 而已,其實就是想自定義一種異常類型,以區別於其餘的異常,方便在框架中進行處理。 框架
第二步:在 Smart Sample 中定義一個 AuthAspect,用於攔截 Action 的全部請求 ide
@Bean @Aspect(pkg = "com.smart.sample.action") @Order(0) public class AuthAspect extends BaseAspect { @Override public boolean filter(Class<?> cls, Method method, Object[] params) { String className = cls.getSimpleName(); String methodName = method.getName(); return !( className.equals("UserAction") && (methodName.equals("login") || methodName.equals("logout") ) ); } @Override public void before(Class<?> cls, Method method, Object[] params) throws Exception { User user = DataContext.Session.get("user"); if (user == null) { throw new AuthException(); } } }
以上代碼中需注意一下幾點: 函數
這樣 AOP 框架(也就是 BaseAspect 類及其相關 Proxy Chain 等)就能夠處理這個自定義異常了。 性能
還記得 Order 註解嗎?它曾經在單元測試中出現過,如今還能夠用於定義 AOP 順序。 單元測試
友情提示:
第三步:在 BaseAspect 中將異常繼續往上拋,拋給它的調用者
public abstract class BaseAspect implements Proxy { @Override public final void doProxy(ProxyChain proxyChain) throws Exception { ... begin(); try { if (filter(cls, method, params)) { before(cls, method, params); Object result = proxyChain.doProxyChain(); after(cls, method, params, result); } else { proxyChain.doProxyChain(); } } catch (Exception e) { error(cls, method, params, e); throw e; // 將異常繼續往上拋,拋給它的調用者 } finally { end(); } } ... }
那麼 Aspect 的調用者是誰呢?也就是說,誰用 Aspect 呢?至少 Action 是一個關鍵性用戶。
那麼 Action 又是誰來調用呢?固然就是 DispatchServlet 了。
因而,順騰摸瓜,找到了調用的起源。
第四步:修改 DispatchServlet,處理 AuthException
@WebServlet("/*") public class DispatcherServlet extends HttpServlet { ... private void handleActionMethod(HttpServletRequest request, HttpServletResponse response, ActionBean actionBean, List<Object> paramList) { // 從 ActionBean 中獲取 Action 相關屬性 Class<?> actionClass = actionBean.getActionClass(); Method actionMethod = actionBean.getActionMethod(); // 從 BeanHelper 中建立 Action 實例 Object actionInstance = BeanHelper.getInstance().getBean(actionClass); // 調用 Action 方法 Object actionMethodResult; try { actionMethod.setAccessible(true); // 取消類型安全檢測(可提升反射性能) actionMethodResult = actionMethod.invoke(actionInstance, paramList.toArray()); } catch (Exception e) { // 處理 Action 方法異常【★】 handleActionMethodException(request, response, e); // 直接返回 return; } // 處理 Action 方法返回值 handleActionMethodReturn(request, response, actionMethodResult); } private void handleActionMethodException(HttpServletRequest request, HttpServletResponse response, Exception e) { if (e.getCause() instanceof AuthException) { // 若爲認證異常,則分兩種狀況進行處理 if (WebUtil.isAJAX(request)) { // 若爲 AJAX 請求,則發送 403 錯誤 WebUtil.sendError(403, response); } else { // 不然重定向到首頁 WebUtil.redirectRequest(request.getContextPath() + "/", response); } } else { // 若爲其餘異常,則記錄錯誤日誌 logger.error("調用 Action 方法出錯!", e); } } ... }
你們能夠看到以上代碼的【★】處,也就是調用 Action 方法時的異常處理代碼,請見下面的 handleActionMethodException 方法。邏輯以下:
以上代碼中涉及到了幾個關鍵的 WebUtil 方法,其實這些都是對 Servlet API 的一個簡單的封裝。代碼片斷以下:
public class WebUtil { ... // 重定向請求 public static void redirectRequest(String path, HttpServletResponse response) { try { response.sendRedirect(path); } catch (Exception e) { logger.error("重定向請求出錯!", e); throw new RuntimeException(e); } } // 發送錯誤代碼 public static void sendError(int code, HttpServletResponse response) { try { response.sendError(code); } catch (Exception e) { logger.error("發送錯誤代碼出錯!", e); throw new RuntimeException(e); } } // 判斷是否爲 AJAX 請求 public static boolean isAJAX(HttpServletRequest request) { return request.getHeader("X-Requested-With") != null; } }
以上能夠看到,普通請求很是簡單,直接 redirect 就好了。而對於 AJAX 請求回調問題,咱們可以使用 jQuery 來輕鬆實現。
第五步:使用 jQuery 來處理 AJAX 非法訪問
/* 全局變量 */ var BASE = '/smart-sample'; // 應用 Context 名稱(若爲空字符串表示應用以 ROOT 來發布) ... $(function() { $.ajaxSetup({ cache: false, error: function(jqXHR, textStatus, errorThrown) { switch (jqXHR.status) { case 403: document.write(''); location.href = BASE + '/'; break; case 503: alert(errorThrown); break; } } }); ... }
首先,定義一個全局變量 BASE,表明應用的 Context 名稱,用戶可自行修改。
而後,進行 AJAX 全局設置(使用了 jQuery 的 $.ajaxSetup 方法),在 error 回調函數中處理 403 錯誤,故意清空頁面內容,並返回到應用首頁(效果與普通請求非法訪問時相同)。
以上就是 Smart Framework 關於安全性控制的解決方案,可否使用更簡單更高效的方式來實現?等候您的評論。