Spring Security框架使用過程中會有很多Restful API的攔截操作,像各種過濾器攔截器以及Spring AOP的使用,所以在學習Spring Security之前很有必要對他們進行一個簡單介紹。
過濾器(Filter)
簡介
- JavaWeb三大組件(Servlet、Filter、Listener)之一,Web開發人員通過Filter技術,對web服務器管理的
所有web資源(例如Jsp、Servlet、靜態圖片文件或靜態html文件等)進行攔截
,從而實現一些特殊的功能。
使用場景
- 主要用於對用戶請求進行
預處理
,也可以對HttpServletResponse進行後處理
。 - 使用Filter的
完整流程
:Filter對用戶請求進行預處理,接着將請求交給Servlet進行處理並生成響應,最後Filter再對服務器響應進行後處理。
簡單演示
/**
* 過濾器演示:直接定義一個類實現Filter接口,記得需要配置該過濾器,或者直接加@Component自動配置
*
* @author HuaDong
* @date 2020/4/11 18:38
*/
@Component
public class TimeFilter implements Filter {
/**
* 容器銷燬的時候調用
*/
@Override
public void destroy() {
System.out.println("time filter destroy");
}
/**
* Web服務器每次在調用Web資源之前,都會先調用filter的doFilter方法
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("time filter start");
long start = System.currentTimeMillis();
chain.doFilter(request, response);
System.out.println("time filter 耗時:" + (System.currentTimeMillis() - start));
System.out.println("time filter finish");
}
/**
* 容器啓動時候調用
*/
@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("time filter init");
}
}
攔截器(Interceptor)
簡介
- Java 裏的攔截器是動態攔截 action 調用的對象,它提供了一種機制可以使開發者可以定義在一個 action 執行的前後執行的代碼,也可以在一個 action 執行前阻止其執行,同時也提供了一種可以提取 action 中可重用部分的方式。通俗來說就是
可以攔截一個請求
,對請求進行Controller方法的調用之前之後或者拋出異常時的一個自定義的操作。
使用場景
- 可以對請求進行一個
登錄憑證的校驗
。 - 可以對請求響應之後
加上一些統一的響應字段
等。
簡單演示
- 聲明攔截器
/**
* 攔截器演示:定義一個類實現HandlerInterceptor接口
*
* @author HuaDong
* @date 2020/4/11 19:21
*/
@Component
public class TimeInterceptor implements HandlerInterceptor {
/**
* 請求Controller方法之前:返回false表示被攔截不再往下執行,即不再調用Controller方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("preHandle");
System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
System.out.println(((HandlerMethod)handler).getMethod().getName());
request.setAttribute("startTime", System.currentTimeMillis());
return true;
}
/**
* 請求Controller方法之後
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
Long start = (Long) request.getAttribute("startTime");
System.out.println("time interceptor 耗時:"+ (System.currentTimeMillis() - start));
}
/**
* 拋出異常之後 postHandle 方法不會被調用,無論是否拋出異常 afterCompletion 方法都會被調用
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion");
Long start = (Long) request.getAttribute("startTime");
System.out.println("time interceptor 耗時:"+ (System.currentTimeMillis() - start));
System.out.println("ex is "+ex);
}
}
- 註冊攔截器
/**
* 註冊攔截器:創建一個config類,繼承WebMvcConfigurerAdapter,將我們上面定義的攔截器註冊到InterceptorRegistry中
*
* @author HuaDong
* @date 2020/4/11 19:32
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
缺點
- preHandle方法無法通過handler拿到一些
封裝之後的請求參數
,因爲封裝參數的方法是在preHandle之後進行的。 - 查看源碼:org.springframework.web.servlet.DispatcherServlet#doService --> doDispatch
// 實際 preHandler 的調用
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 參數封裝處理邏輯
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
切片(Aspect)
簡介
- AOP,
面向切面編程
,採用一種稱爲“橫切”的技術,將涉及多業務流程的通用功能抽取並單獨封裝,形成獨立的切面,在合適的時機將這些切面橫向切入到業務流程指定的位置中。
使用場景
- 權限控制
- 緩存控制
- 事務控制
- 審計日誌
- 性能監控
- 分佈式追蹤
- 異常處理