1. 程式人生 > 其它 >SpringBoot&Filter的詳解

SpringBoot&Filter的詳解

SpringBoot&Filter的詳解

過濾器實際上就是對web資源進行攔截,做一些處理後再交給下一個過濾器或servlet處理 通常都是用來攔截request進行處理的,也可以對返回的response進行攔截處理

大概流程圖如下

應用場景

  • 自動登入

  • 統一設定編碼格式

  • 訪問許可權控制

  • 敏感字元過濾等

  • ......


1、Filter的使用

  要想使用filter,需要寫一個方法繼承Filter類,我們寫如下兩個自己的Filter類,首先是FirstFilter類,其中@Order裡邊的數字越小代表越先被該Filter過濾,@WebFilter

代表這是個Filter類並把這個類注入到容器中。

注意:

SpringBoot 實現 Filter 兩種方式:

  1. 自定義Filter通過FilterRegistrationBean 類來注入;(不推薦)

  2. 自定義Filter通過 實現介面 Filter方法,@WebFilter注入。

FirstFilter

@Order(1)
@WebFilter(filterName="firstFilter", urlPatterns="/*")
public class FirstFilter implements Filter {

    @Override
    public void
init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("first filter 1111111111111111111"); chain.doFilter(request, response); System.out.println(
"first filter 2222222222222222222"); } @Override public void destroy() { } }

SecondFilter

@Order(2)
@WebFilter(filterName="secondFilter", urlPatterns="/*")
public class SecondFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("second filter 1=============================");
        System.out.println("before:" + response);
        chain.doFilter(request, response);
        System.out.println("after:" + response);
        System.out.println("second filter 2=============================");

    }

    @Override
    public void destroy() {

    }
}

TestController

@RestController
public class TestController {

    @GetMapping("/test1")
    public String test1() {
        System.out.println("method in controller");
        return "test1";
    }

}

postman測試:http://localhost:8080/test1

輸出結果:

first filter 1111111111111111111
second filter 1=============================
before:org.apache.catalina.connector.ResponseFacade@1d2aafd8
method in controller
after:org.apache.catalina.connector.ResponseFacade@1d2aafd8
second filter 2=============================
first filter 2222222222222222222

過程:

總結:

  我們可以看出程式碼執行的流程,首先請求被firstfilter截獲,打印出first filter 1,然後去執行chain.doFilter(request, response),這句話代表著請求會轉發給過濾器鏈上下一個物件,也就是secondfilter,所以打印出secondfilter裡的second filter 1,接下來再執行secondfilter裡的chain.dofilter()方法,請求再轉發給下一個物件,由於沒有其他的filter了,所以會轉發給controller,打印出了controller類中的method in controller,接下來再去記憶體棧裡呼叫secondfilter的print("second filter 2"),然後再去記憶體棧裡呼叫firstfilter的print("first filter 1")。所以如果在自己實現的Filter類的doFilter方法裡不加chain.doFilter(req, rep)是萬萬不行的,那樣會導致請求到了這個filter裡就不再往下走了,永遠進不了controller中。

  我們也可以在print("before:" + response)和print("after:" + response)這兩個地方打上斷點,然後除錯一下,你會發現在before那裡的response裡是什麼都麼有的,而在after那裡的response裡則是已經有了test1字串,也就是說controller類test1方法的返回值已經新增進了response,所以如果你想對請求的response做一下過濾處理,那麼一定要在chain.doFilter(res, rep)之後寫你的邏輯。


2、@WebFilter註解說明

  @WebFilter 用於將一個類宣告為過濾器,該註解將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署為過濾器。該註解具有下表給出的一些常用屬性 ( 以下所有屬性均為可選屬性,但是 value、urlPatterns、servletNames 三者必需至少包含一個,且 value 和 urlPatterns 不能共存,如果同時指定,通常忽略 value 的取值 )

@WebFilter 的常用屬性

屬性名型別說 明
filterName String 指定過濾器的 name 屬性,等價與 <filter-name> 標籤
value String[] 該屬性等價於 <url-pattern> 標籤,但是兩者不應該同時使用
urlPatterns String[] 指定一組過濾器的 URL 匹配模式。等價於 <url-pattern> 標籤。
servletNames String[] 用於指定過濾的servlet。取值是 @WebServlet 中的 name 屬性的取值,或者是 web.xml 中 <servlet-name> 的取值。
dispatcherTypes DispatcherType[] 指定過濾器的轉發模式。具體取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST
initParams WebInitParam[] 用於設定該過濾器類的一些初始化引數,等同於<init-param> 標籤
asyncSupported Boolean 宣告過濾器是否支援非同步操作模式,等價於 <async supported> 標籤
description String 對該過濾器的描述資訊,等價於 <description> 標籤
displayName String 過濾器顯示名,通常配合工具使用,等同與<display-name> 標籤

3、責任鏈模式的實際應用

Filter和FilterChain都是怎麼用責任鏈模式實現的,自定義實現一下, 使用Filter模式來模擬

Filter介面

public interface Filter {
    /**
     * 過濾器
     * @param request 請求物件
     * @param response 返回物件
     */
    public void doFilter(Request request, Response response, FilterChain chain);

}

FilterChain類 { 過濾器鏈 }

/**
 * @Desc: TODO 過濾器鏈【執行責任鏈模式的主要成員】
 */
public class FilterChain implements Filter {

    /**
     * 過濾器列表
     */
    private List<Filter> filters = new ArrayList<>();

    int index = 0;

    /**
     * 新增過濾器
     * @param filter
     */
    public FilterChain addFilter(Filter filter) {
        filters.add(filter);
        return this;
    }

    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        if(index == filters.size()) {
            /**
             * 真正處理請求(此時開始處理 controller業務邏輯)
             */
            return;
        }
        Filter filter = filters.get(index);
        index++;
        filter.doFilter(request, response, chain);
    }

}

Request類和Response類

public class Request {
    public String requestStr;
}
public class Response {
    public String responseStr;
}

實現類(業務中需要的過濾器,以下舉例)

EncodeFilter{ 設定統一編碼 }

public class EncodeFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        request.requestStr = request.requestStr + "  設定編碼 UTF-8";
        System.out.println("EncodeFilter request Str:" + request.requestStr);
        chain.doFilter(request, response, chain);
        response.responseStr = response.responseStr + "-------------設定編碼 UTF-8";
        System.out.println("EncodeFilter response Str:" + response.responseStr);
    }
}

VerifyParameterFilter{ 引數校驗 }

public class VerifyParameterFilter  implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        request.requestStr = request.requestStr + "  引數驗證 成功";
        System.out.println("VerifyParameterFilter request str:" + request.requestStr);
        chain.doFilter(request, response, chain);
        response.responseStr = response.responseStr + "======================";
        System.out.println("VerifyParameterFilter response str:" + response.responseStr);
    }
}

測試

public class FilterTest {
    public static void main(String[] args) {
        String msg = "張三";
        Request request = new Request();
        request.requestStr = msg;
        Response response = new Response();
        response.responseStr = "success *****";

        FilterChain fc = new FilterChain();
        fc.addFilter(new EncodeFilter())
                .addFilter(new VerifyParameterFilter())
                .doFilter(request, response, fc);
    }
}

結果:

EncodeFilter request Str:張三  設定編碼 UTF-8
VerifyParameterFilter request str:張三  設定編碼 UTF-8  引數驗證 成功
VerifyParameterFilter response str:success *****======================
EncodeFilter response Str:success *****======================-------------設定編碼 UTF-8

總結

  1. Filter是針對請求進行攔截、意在請求前進行一些過濾、許可權校驗、日誌記錄、統一編碼等

  2. 所有Filter統一在FilterChain中組成一個鏈條,然後呼叫也統一由FilterChain來協調,確保以鏈條的模式執行

  3. 責任鏈模式是一種重要的設計模式,如Servlet中的Filter模式、Mybatis中的Plugin模式等都是責任鏈模式的體現