Spring Security技術棧開發企業級認證與授權(五)使用Filter、Interceptor和AOP攔截REST服務
一般情況,在訪問
RESTful
風格的API
之前,可以對訪問行為進行攔截,並做一些邏輯處理,本文主要介紹三種攔截方式,分別是:過濾器Filter
、攔截器Interceptor
以及面向切面的攔截方式AOP
。
一、使用過濾器Filter進行攔截
使用過濾器進行攔截主要有兩種方式,第一種是將自定義的攔截器標註為Spring
的Bean
,在Spring Boot
應用就可以對RESTful
風格的API
進行攔截。第二種方式往往應用在繼承第三方過濾器,這時候就需要將第三方攔截器使用FilterRegistrationBean
物件進行註冊即可。接下來詳細介紹兩種方式。
- 將攔截器標註為
Spring
Bean
package com.lemon.security.web.filter;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import java.io.IOException;
/**
* @author lemon
* @date 2018/4/1 下午10:19
*/
@Component
public class TimeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("time filter init.");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("time filter start.");
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
System.out.println("time filter 耗時: " + (System.currentTimeMillis() - startTime));
System.out.println("time filter finish.");
}
@Override
public void destroy() {
System.out.println("time filter destroy.");
}
}
啟動Spring Boot
應用的時候,上面的攔截器就會起作用,當訪問每一個服務的時候,都會進入這個攔截器中。初始化方法init
和銷燬方法destroy
只會呼叫一次,分別是應用啟動時候呼叫init
方法,應用關閉時候呼叫destroy
方法。而doFilter
方法則在每次都會呼叫。
- 將攔截器作為第三方攔截器進行註冊
使用的類還是上面的同一個類,只不過這次不需要@Component
註解,這時候我們需要自己寫一個配置類,將過濾器註冊到Spring
容器中。推薦使用這種方式,因為這種方式我們可以自己設定需要攔截的API
,否則第一種方式是攔截所有的API
。
package com.lemon.security.web.config;
import com.lemon.security.web.filter.TimeFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* @author lemon
* @date 2018/4/1 下午10:34
*/
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean timeFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
TimeFilter timeFilter = new TimeFilter();
filterRegistrationBean.setFilter(timeFilter);
List<String> urls = new ArrayList<>();
urls.add("/*");
filterRegistrationBean.setUrlPatterns(urls);
return filterRegistrationBean;
}
}
這裡我設定的仍然是攔截所有的API
,可以設定為自定義的方式對API
進行攔截。
二、使用攔截器Interceptor進行攔截
這裡需要定義一個攔截器類,並實現HandlerInterceptor介面,這個介面有三個方法需要實現,分別是:
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
下面對三個方法進行一一解釋:
preHandle
方法的第三個引數是具體的API
處理方法的Method
物件,我們可以將其強轉為HandlerMethod
,然後就可以獲取該Method
的一些屬性,比如方法名,方法所在類的類名等資訊。preHandle
是當訪問API
之前,都要進入這個方法,由這個方法進行一些邏輯處理,如果處理完結果返回true
,那麼將繼續進入到具體的API
中,否則將就地結束訪問,邏輯不會進入API
方法中。postHandle
方法是在API
方法訪問完成之後立即進入的方法,可以處理一些邏輯,比如將API
中的資料封裝到ModelAndView
中,如果前面的preHandle
方法返回false
,將不會執行該方法,如果API
方法發生了異常,也將不會呼叫此方法。afterCompletion
方法的呼叫只要preHandle
方法通過之後就會呼叫它,不論API
方法是否出現了異常。如果出現了異常,將被封裝到Exception
物件中。
下面,寫一個自定義的類來實現上述介面:
package com.lemon.security.web.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author lemon
* @date 2018/4/1 下午10:39
*/
@Component
public class TimeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandler");
System.out.println(((HandlerMethod) handler).getBean().getClass().getName());
System.out.println(((HandlerMethod) handler).getMethod().getName());
request.setAttribute("startTime", System.currentTimeMillis());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandler");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
System.out.println("TimeInterceptor耗時:" + (System.currentTimeMillis() - (Long) request.getAttribute("startTime")));
}
}
這裡需要將其標註為Spring
的Bean
,但是僅僅標註為Bean
還是不夠的,需要在配置類中進行配置。程式碼如下:
package com.lemon.security.web.config;
import com.lemon.security.web.interceptor.TimeInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author lemon
* @date 2018/4/1 下午10:34
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
這個配置類需要繼承WebMvcConfigurerAdapter
,並重寫新增攔截器的方法addInterceptors
,將自定義攔截器新增到應用中。這時候攔截器就生效了。
三、使用AOP進行攔截
其實是有攔截器Interceptor
對API
進行攔截的時候是有缺陷的,因為無法獲取前端訪問API
的時候所攜帶的引數的,為什麼會這麼說?從Spring MVC
的DispatcherServlet
的原始碼中可以發現,找到doDispatch
方法,也就是請求分發的方法,有一段程式碼如下:
如果我們自定的Interceptor
的preHandler
方法返回的是false
,分發任務就會截止,不再繼續執行下面的程式碼,而下面的一行程式碼正是將前端攜帶的引數進行對映的邏輯,也就是說,preHandler
方法不會接觸到前端攜帶來的引數,也就是說攔截器無法處理引數。所以這裡引進AOP
進行攔截。
AOP的核心概念解釋:
描述AOP常用的一些術語有通知(Adivce
)、切點(Pointcut
)、連線點(Join point
)、切面(Aspect
)、引入(Introduction
)、織入(Weaving
)
- 通知(
Advice
)
通知分為五中型別:
Before
:在方法被呼叫之前呼叫
After
:在方法完成後呼叫通知,無論方法是否執行成功
After-returning
:在方法成功執行之後呼叫通知
After-throwing
:在方法丟擲異常後呼叫通知
Around
:通知了好、包含了被通知的方法,在被通知的方法呼叫之前後呼叫之後執行自定義的行為
- 連線點(
Join point
)
連線點是一個應用執行過程中能夠插入一個切面的點。比如:方法呼叫、方法執行、欄位設定/獲取、異常處理執行、類初始化、甚至是for
迴圈中的某個點。理論上, 程式執行過程中的任何時點都可以作為作為織入點, 而所有這些執行時點都是Joint point
,但 Spring AOP
目前僅支援方法執行 (method execution
)。
- 切點(
Pointcut
)
通知(advice
)定義了切面何時,那麼切點就是定義切面“何處” 描述某一類 Joint points
, 比如定義了很多 Joint point
, 對於 Spring AOP
來說就是匹配哪些方法的執行。
- 切面(
Aspect
)
切面是切點和通知的結合。通知和切點共同定義了關於切面的全部內容,它是什麼時候,在何時和何處完成功能。
- 引入(
Introduction
)
引用允許我們向現有的類新增新的方法或者屬性
- 織入(
Weaving
)
組裝方面來建立一個被通知物件。這可以在編譯時完成(例如使用AspectJ
編譯器),也可以在執行時完成。Spring
和其他純Java AOP
框架一樣,在執行時完成織入。
上面的概念有點生澀難懂,總結一個核心內容:切面 = 切點 + 通知
。
現在通過程式碼來編寫一個切面:
package com.lemon.security.web.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @date 2018/4/2 上午10:40
*/
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.lemon.security.web.controller.UserController.*(..))")
public Object handleTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("time aspect is start.");
for (Object object : proceedingJoinPoint.getArgs()) {
System.out.println(object);
}
long startTime = System.currentTimeMillis();
Object obj = proceedingJoinPoint.proceed();
System.out.println("time aspect 耗時:" + (System.currentTimeMillis() - startTime));
System.out.println("time aspect finish.");
return obj;
}
}
@Around
定義了環繞通知,也就是定義了何時使用切面,表示式"execution(* com.lemon.security.web.controller.UserController.*(..))"
定義了再哪裡使用。ProceedingJoinPoint
物件的proceed()
方法表示執行被攔截的方法,它有一個Object
型別的返回值,是原有方法的返回值,後期使用的時候往往需要強轉。關於切點的表示式,可以訪問Spring官方文件。
對於上面三種攔截方式,他們的執行有一個基本的順序,進入的順序是Filter-->Interceptor-->Aspect-->Controller-->Aspect-->Interceptor-->Filter
(不考慮異常的發生)。如下圖所示:
Spring Security技術棧開發企業級認證與授權系列文章列表:
示例程式碼下載地址:
專案已經上傳到碼雲,歡迎下載,內容所在資料夾為
chapter005
。
相關推薦
Spring Security技術棧開發企業級認證與授權(五)使用Filter、Interceptor和AOP攔截REST服務
一般情況,在訪問RESTful風格的API之前,可以對訪問行為進行攔截,並做一些邏輯處理,本文主要介紹三種攔截方式,分別是:過濾器Filter、攔截器Interceptor以及面向切面的攔截方式AOP。 一、使用過濾器Filter進行攔截 使用過
Spring Security技術棧開發企業級認證與授權(九)開發圖形驗證碼介面
在設計登入模組的時候,圖形驗證碼基本上都是標配,本篇部落格重點介紹開發可重用的圖形驗證碼介面,該介面支援使用者自定義配置,比如驗證碼的長度、驗證碼圖形的寬度和高度等資訊。 本文的目標是開發一個圖形驗證碼介面,該驗證碼支援使用者自定義長度,以及生成圖片後
Spring Security技術棧開發企業級認證與授權(八)Spring Security的基本執行原理與個性化登入實現
正如你可能知道的兩個應用程式的兩個主要區域是“認證”和“授權”(或者訪問控制)。這兩個主要區域是Spring Security的兩個目標。“認證”,是建立一個他宣告的主題的過程(一個“主體”一般是指使用者,裝置或一些可以在你的應用程式中執行動作的其他系統)
Spring Security技術棧開發企業級認證與授權(七)使用Swagger自動生成API文件
由於Spring Boot能夠快速開發、便捷部署等特性,相信有很大一部分Spring Boot的使用者會用來構建RESTful API。而我們構建RESTful API的目的通常都是由於多終端的原因,這些終端會共用很多底層業務邏輯,因此我們會抽象出這樣一層
Spring Security技術棧開發企業級認證與授權(一)環境搭建
Spring Security是一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Contr
Spring Security技術棧開發企業級認證與授權(十一)開發簡訊驗證碼登入
簡訊登入也是一種常見的登入方式,但是簡訊登入的方式並沒有整合到Spring Security中,所以往往還需要我們自己開發簡訊登入邏輯,將其整合到Spring Security中,使用Spring Security來進行校驗。本文將介紹開發簡訊登入的方法,
Spring Security技術棧開發企業級認證與授權
iyu 復雜 sha 日誌 開發app 一個 核心概念 並發 自動 Spring Security技術棧開發企業級認證與授權網盤地址:https://pan.baidu.com/s/1mj8u6JQ 密碼: 92rp備用地址(騰訊微雲):https://share.weiy
Spring Security技術棧開發企業級認證與授權 Spring Security開發安全的REST服務
第1章 課程導學 介紹課程內容、課程特點,使用的主要技術棧,以及學習課程所需的前置知識 1-1 導學 第2章 開始開發 安裝開發工具,介紹專案程式碼結構並搭建,基本的依賴和引數設定,開發hello world 2-1 開發環境安裝 2-2 程式碼結構介紹 2-3
.net core 認證與授權(一)
前言 .net core web並不是一個非常新的架構,很多文章提及到認證與授權這個過程,但是一般都會提及到裡面的方法怎麼用的,而不是模擬一個怎樣的過程,所以我打算記錄自己的理解。 什麼是認證?我們大學畢業有學士證書和畢業證書,來證明你是一個學士。 什麼是授權,比如說你被認證是我的朋友後,你可以拿著這個身份,
.net core 認證與授權(二)
前言 這篇緊接著一來寫的,在第一篇中介紹了認證與授權,同時提出了這套機制其實就是模擬現實中的認證與授權。 同樣這篇介紹在這套機制下,使用者資訊管理機制?這裡就會問了,上一篇中認證和授權不是都ok了嗎,怎麼會有一個管理機制呢?當然並不一定要使用下面這套機制,但是給了我們很大的啟發。 在上一結中我們頒發證書是這樣
.net core 認證與授權(三)
前言 在寫三上是在一的基礎上寫的,所以有沒有看過二是沒得關係的,在一中介紹了認證與授權,但是沒有去介紹拿到證書後怎樣去驗證授權。 概念性東西:在這套機制中,把這個許可權認證呢,稱作為policy。這個policy是怎麼樣的過程呢? 就像我前面說的,證書也認證了,policy做的是檢查你的證書中是否符合我心中的
Angular SPA基於Ocelot API閘道器與IdentityServer4的身份認證與授權(四)
在上一講中,我們已經完成了一個完整的案例,在這個案例中,我們可以通過Angular單頁面應用(SPA)進行登入,然後通過後端的Ocelot API閘道器整合IdentityServer4完成身份認證。在本講中,我們會討論在當前這種架構的應用程式中,如何完成使用者授權。 回顧 《Angular SPA基於Oc
全棧開發之HTML快速入門(一)
ack enter 提示 其他 red tle 顯示圖片 val password 一、HTML 是什麽? HTML 指的是超文本標記語言 (Hyper Text Markup Language) HTML 不是一種編程語言,而是一種標記語言 (markup lan
2016-06-26 發布 支付系統開發的實踐與思考(一)
接口 簡單的 單向 new 成了 異步通知 平臺 應收 技術分享 通常我們在開發手機 app 或網站時都會涉及到支付相關的業務場景,用戶只需要簡單的點擊下按鈕並輸入密碼,就完成了整個支付過程。那麽今天我們就來簡單聊一下一個完整的支
資料結構與演算法(六)-揹包、棧和佇列
前言:許多基礎資料型別都和物件的集合有關。具體來說,資料型別的值就是一組物件的集合,所有操作都是關於新增、刪除或是訪問集合中的物件。而且有很多高階資料結構都是以這樣的結構為基石創造出來的,在本文中,我們將瞭解學習三種這樣的資料型別,分別是揹包(Bag)、棧(Stack)和佇列(Queue) 一、學習感悟
Android開發--圖形影象與動畫(四)--AnimationListener簡介
就像Button控制元件有監聽器一樣,動畫效果也有監聽器,只需要實現AnimationListener就可以實現對動畫效果的監聽,其中需要過載三個函式,就是下面的這幾個函式:private class MyListenr implements AnimationLis
Android開發中佈局與元件(二)—— padding 與 margin 的區別
在 Android開發中我們會設定某個檢視相對於別的檢視的距離,這時我們就要用到 margin 和 padding ,但是有時候很容易把這兩個屬性弄混淆,那我們就看看他們的區別。 外邊距(margin): 屬於佈局引數,決定兩個元件之間的距離。作用於多個元件之間。 內邊距(
Android開發中佈局與元件(一)—— 螢幕尺寸單位dp,px,sp的探究
在Android開發中,常用的尺寸單位有 dp , px , sp 。當然還有其他的單位如 pt , mm 等,不過這些都是不常用,所以我們重點來探究一下 dp , px , sp 這三個常用的單位。 px 英文 pixel 的縮寫,即畫素。無論螢幕密度為多少,一個畫素單位對應
Android開發與Linux開發的區別與聯絡(入門)
1、Android的目錄路徑等不明,Linux程式碼如何移植進Android相應目錄不知; 2、目前Linux核心程式碼放置在\\192.168.1.190\share\android\kernel_imx; 3、安卓下編譯Linux核心的方法(PC機使用ssh除錯): #
python全棧開發中級班全程筆記(第二模塊、第四章)模塊(常用模塊)
span 自己 文件目錄 port all mode 功能 最大的 維護 python全棧開發筆記第二模塊 第四章 :常用模塊 一、定義: 在程序開發過程中,隨著代碼越寫越多,在一個文件裏,代碼就會越來越長,越來越不容易維護。 為了編寫更好維護的