溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

SpringBoot中過濾器Filter怎么用

發(fā)布時(shí)間:2021-09-08 15:21:03 來源:億速云 閱讀:177 作者:小新 欄目:大數(shù)據(jù)

小編給大家分享一下SpringBoot中過濾器Filter怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

I. 背景

在正式開始之前,有必要先簡單看一下什么是Filter(過濾器),以及這個(gè)有什么用

1. Filter說明

Filter,過濾器,屬于Servlet規(guī)范,并不是Spring獨(dú)有的。其作用從命名上也可以看出一二,攔截一個(gè)請(qǐng)求,做一些業(yè)務(wù)邏輯操作,然后可以決定請(qǐng)求是否可以繼續(xù)往下分發(fā),落到其他的Filter或者對(duì)應(yīng)的Servlet

簡單描述下一個(gè)http請(qǐng)求過來之后,一個(gè)Filter的工作流程:

  • 首先進(jìn)入filter,執(zhí)行相關(guān)業(yè)務(wù)邏輯

  • 若判定通行,則進(jìn)入Servlet邏輯,Servlet執(zhí)行完畢之后,又返回Filter,最后在返回給請(qǐng)求方

  • 判定失敗,直接返回,不需要將請(qǐng)求發(fā)給Servlet

插播一句:上面這個(gè)過程,和AOP中的@Around環(huán)繞切面的作用差不多

2. 項(xiàng)目搭建

接下來我們搭建一個(gè)web應(yīng)用方便后續(xù)的演示,借助SpringBoot搭建一個(gè)web應(yīng)用屬于比較簡單的活;

創(chuàng)建一個(gè)maven項(xiàng)目,pom文件如下

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7</version>
    <relativePath/> <!-- lookup parent from update -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.45</version>
    </dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

II. Filter教程

1. 使用說明

在SpringBoot項(xiàng)目中,如果需要自定義一個(gè)Filter,并沒有什么特殊的地方,直接實(shí)現(xiàn)接口即可,比如下面一個(gè)輸出請(qǐng)求日志的攔截器

@Slf4j
@WebFilter
public class ReqFilter implements Filter {
    public ReqFilter() {
        System.out.println("init reqFilter");
    }

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        log.info("url={}, params={}", req.getRequestURI(), JSON.toJSONString(req.getParameterMap()));
        chain.doFilter(req, response);
    }

    @Override
    public void destroy() {
    }
}

實(shí)現(xiàn)一個(gè)自定義的Filter容易,一般有兩個(gè)步驟

  • 實(shí)現(xiàn) Filter 接口

  • doFilter方法中添加業(yè)務(wù)邏輯,如果允許訪問繼續(xù),則執(zhí)行chain.doFilter(req, response);; 不執(zhí)行上面這一句,則訪問到此為止

接下來的一個(gè)問題就是如何讓我們自定義的Filter生效,在SpringBoot項(xiàng)目中,有兩種常見的使用方式

  • @WebFilter

  • 包裝Bean: FilterRegistrationBean

a. WebFilter

這個(gè)注解屬于Servlet3+,與Spring也沒有什么關(guān)系,所以問題來了,當(dāng)我在Filter上添加了這個(gè)注解之后,Spring怎么讓它生效呢?

  • 配置文件中顯示使用注解 @ServletComponentScan

@ServletComponentScan
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

WebFilter常用屬性如下,其中urlPatterns最為常用,表示這個(gè)filter適用于哪些url請(qǐng)求(默認(rèn)場(chǎng)景下全部請(qǐng)求都被攔截)

屬性名類型描述
filterNameString指定過濾器的 name 屬性,等價(jià)于 <filter-name>
valueString[]該屬性等價(jià)于 urlPatterns 屬性。但是兩者不應(yīng)該同時(shí)使用。
urlPatternsString[]指定一組過濾器的 URL 匹配模式。等價(jià)于 <url-pattern> 標(biāo)簽。
servletNamesString[]指定過濾器將應(yīng)用于哪些 Servlet。取值是 @WebServlet 中的 name 屬性的取值,或者是 web.xml 中<servlet-name> 的取值。
dispatcherTypesDispatcherType指定過濾器的轉(zhuǎn)發(fā)模式。具體取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。
initParamsWebInitParam[]指定一組過濾器初始化參數(shù),等價(jià)于 <init-param> 標(biāo)簽。
asyncSupportedboolean聲明過濾器是否支持異步操作模式,等價(jià)于 <async-supported> 標(biāo)簽。
descriptionString該過濾器的描述信息,等價(jià)于 <description> 標(biāo)簽。
displayNameString該過濾器的顯示名,通常配合工具使用,等價(jià)于 <display-name> 標(biāo)簽。
b. FilterRegistrationBean

上面一種方式比較簡單,后面會(huì)說到有個(gè)小問題,指定Filter的優(yōu)先級(jí)比較麻煩,

下面是使用包裝bean注冊(cè)方式

@Bean
public FilterRegistrationBean<OrderFilter> orderFilter() {
    FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>();
    filter.setName("reqFilter");
    filter.setFilter(new ReqFilter());
    // 指定優(yōu)先級(jí)
    filter.setOrder(-1);
    return filter;
}

2. 常見問題

上面整完,就可以開始測(cè)試使用過濾器了,在進(jìn)入實(shí)測(cè)環(huán)節(jié)之前,先來看兩個(gè)常見的問題

  • Filter作為Servelt的組件,怎么與SpringBoot中的Bean交互

  • 多個(gè)Filter之間的優(yōu)先級(jí)怎么確定

a. Filter依賴Bean注入問題

如果有小伙伴使用SpringMVC + web.xml方式來定義Filter,就會(huì)發(fā)現(xiàn)自定義的Filter中無法通過@Autowired方式來注入Spring的bean

我之前使用的是spring4 Servlet2+ ,存在上面的問題,如果有不同觀點(diǎn)請(qǐng)留言告訴我,感謝

SpringBoot中可以直接注入依賴的Bean,從上面的第二種注冊(cè)方式可以看到,Spring將Filter封裝成了一個(gè)Bean對(duì)象,因此可以直接注入依賴的Bean

下面定義一個(gè)AuthFilter,依賴了自定義的DemoBean

@Data
@Component
public class DemoBean {
    private long time;

    public DemoBean() {
        time = System.currentTimeMillis();
    }

    public void show() {
        System.out.println("demo bean!!! " + time);
    }
}


@Slf4j
@WebFilter
public class AuthFilter implements Filter {
    @Autowired
    private DemoBean demoBean;

    public AuthFilter() {
        System.out.println("init autFilter");
    }

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

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        log.info("in auth filter! {}", demoBean);
        // 測(cè)試,用header中的 tx-demo 來判斷是否為認(rèn)證的請(qǐng)求
        HttpServletRequest req = (HttpServletRequest) request;
        String auth = req.getHeader("tx-demo");
        if ("yihuihui".equals(auth)) {
            // 只有認(rèn)證的請(qǐng)求才允許訪問,請(qǐng)求頭中沒有這個(gè)時(shí),不執(zhí)行下面的的方法,則表示請(qǐng)求被過濾了
            // 在測(cè)試優(yōu)先級(jí)時(shí)打開下面的注釋
            // chain.doFilter(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {

    }
}
b. 優(yōu)先級(jí)指定

Filter的優(yōu)先級(jí)指定,通過我的實(shí)際測(cè)試,@Order注解沒有用,繼承 Ordered接口也沒有用,再不考慮web.xml的場(chǎng)景下,只能通過在注冊(cè)Bean的時(shí)候指定優(yōu)先級(jí)

實(shí)例如下,三個(gè)Filter,兩個(gè)通過@WebFilter注解方式注冊(cè),一個(gè)通過FilterRegistrationBean方式注冊(cè)

@Slf4j
@Order(2)
@WebFilter
public class AuthFilter implements Filter, Ordered {
  ...
}

@Slf4j
@Order(1)
@WebFilter
public class ReqFilter implements Filter, Ordered {
  ...
}

@Slf4j
public class OrderFilter implements Filter {
}

@ServletComponentScan
@SpringBootApplication
public class Application {
    @Bean
    public FilterRegistrationBean<OrderFilter> orderFilter() {
        FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>();
        filter.setName("orderFilter");
        filter.setFilter(new OrderFilter());
        filter.setOrder(-1);
        return filter;
    }


    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

}

3. 測(cè)試

上面定義了三個(gè)Filter,我們主要驗(yàn)證下優(yōu)先級(jí),如果@Order注解生效,那么執(zhí)行的先后順序應(yīng)該是

OrderFilter -> ReqFilter -> AuthFilter

如果不是上面的順序,那么說明@Order注解沒有用

@RestController
public class IndexRest {
    @GetMapping(path = {"/", "index"})
    public String hello(String name) {
        return "hello " + name;
    }
}

SpringBoot中過濾器Filter怎么用

(上文截圖源碼來自: org.apache.catalina.core.ApplicationFilterFactory#createFilterChain

上面是測(cè)試時(shí)關(guān)鍵鏈路的斷點(diǎn)截圖,從數(shù)組中可以看出 AuthFilter的優(yōu)先級(jí)大于ReqFilter, 下面實(shí)際的輸出也說明了@Order注解不能指定Filter的優(yōu)先級(jí)(不知道為什么網(wǎng)絡(luò)上有大量使用Order來指定Filer優(yōu)先級(jí)的文章!!!)

SpringBoot中過濾器Filter怎么用

接下來我們的問題就是WebFilter注解來注冊(cè)的Filter的優(yōu)先級(jí)是怎樣的呢,我們依然通過debug來看,關(guān)鍵代碼路徑為: org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize

SpringBoot中過濾器Filter怎么用

  • OrderFiler是我們手動(dòng)注冊(cè)并設(shè)置優(yōu)先級(jí)為-1

  • ReqFilter, AuthFilter通過 WebFillter方式注冊(cè),默認(rèn)優(yōu)先級(jí)為2147483647,相同優(yōu)先級(jí)的情況下,根據(jù)名字先后順序來決定

III. 小結(jié)

本文主要介紹了過濾器Filter的使用方式,以及常見的兩個(gè)問題解答,文中內(nèi)容穿插了一點(diǎn)源碼的分析截圖,并未深入,如有興趣的同學(xué)可以根據(jù)文中提的幾個(gè)關(guān)鍵位置探索一番

下面簡單小結(jié)下文中內(nèi)容

1. Filter使用

自定義Filter的實(shí)現(xiàn)

  • 實(shí)現(xiàn)Filter接口

  • doFilter方法中,顯示調(diào)用chain.doFilter(request, response);表示請(qǐng)求繼續(xù);否則表示請(qǐng)求被過濾

注冊(cè)生效

  • @ServletComponentScan自動(dòng)掃描帶有@WebFilter注解的Filter

  • 創(chuàng)建Bean: FilterRegistrationBean 來包裝自定義的Filter

2. IoC/DI

在SpringBoot中Filter可以和一般的Bean一樣使用,直接通過Autowired注入其依賴的Spring Bean對(duì)象

3. 優(yōu)先級(jí)

通過創(chuàng)建FilterRegistrationBean的時(shí)候指定優(yōu)先級(jí),如下

@Bean
public FilterRegistrationBean<OrderFilter> orderFilter() {
    FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>();
    filter.setName("orderFilter");
    filter.setFilter(new OrderFilter());
    filter.setOrder(-1);
    return filter;
}

此外格外注意, @WebFilter聲明的Filter,優(yōu)先級(jí)為2147483647(最低優(yōu)先級(jí))

  • @Order注解不能指定Filter優(yōu)先級(jí)

  • @Order注解不能指定Filter優(yōu)先級(jí)

  • @Order注解不能指定Filter優(yōu)先級(jí)

以上是“SpringBoot中過濾器Filter怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI