SpringBoot使用Filters實(shí)現(xiàn)請(qǐng)求過(guò)濾和預(yù)處理
什么是過(guò)濾器
過(guò)濾器(Filter)是一種在Web應(yīng)用中用于攔截和處理HTTP請(qǐng)求和響應(yīng)的對(duì)象。
在Java Web開(kāi)發(fā)中,過(guò)濾器是實(shí)現(xiàn)特定功能,如認(rèn)證、日志記錄和字符編碼處理的重要工具。具體如下:
- 過(guò)濾器的基本概念
- 定義:過(guò)濾器是一種可以動(dòng)態(tài)地?cái)r截傳入的請(qǐng)求和傳出的響應(yīng),并對(duì)這些請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理和后處理的對(duì)象。
- 作用:過(guò)濾器可以檢查和修改請(qǐng)求頭和響應(yīng)頭、處理cookies、驗(yàn)證用戶提交的數(shù)據(jù)等。
- 過(guò)濾器的工作原理
- 工作流程:當(dāng)一個(gè)請(qǐng)求到達(dá)服務(wù)器時(shí),它會(huì)首先通過(guò)過(guò)濾器鏈,每個(gè)過(guò)濾器都有機(jī)會(huì)對(duì)請(qǐng)求進(jìn)行處理。如果請(qǐng)求通過(guò)了所有過(guò)濾器的檢驗(yàn),它最終會(huì)到達(dá)目標(biāo)資源,如servlet或JSP頁(yè)面。
- 請(qǐng)求處理:在過(guò)濾器中可以通過(guò)修改請(qǐng)求對(duì)象來(lái)實(shí)現(xiàn)對(duì)請(qǐng)求數(shù)據(jù)的預(yù)處理,例如,可以對(duì)輸入數(shù)據(jù)進(jìn)行解碼或者驗(yàn)證。
- 響應(yīng)處理:在放行請(qǐng)求后,還可以在過(guò)濾器中修改返回給用戶的響應(yīng)數(shù)據(jù),例如,對(duì)輸出數(shù)據(jù)進(jìn)行編碼或者壓縮。
- 過(guò)濾器的生命周期
- 初始化:
init
方法在過(guò)濾器創(chuàng)建時(shí)被調(diào)用,用于初始化操作。這個(gè)方法只運(yùn)行一次,一般用來(lái)讀取配置文件和初始化資源。 - 過(guò)濾請(qǐng)求:
doFilter
方法是實(shí)際執(zhí)行過(guò)濾操作的地方,每次請(qǐng)求經(jīng)過(guò)過(guò)濾器時(shí)都會(huì)被調(diào)用。在這個(gè)方法中可以實(shí)現(xiàn)具體的過(guò)濾邏輯,并通過(guò)FilterChain
對(duì)象決定是否將請(qǐng)求傳遞到下一個(gè)過(guò)濾器或者目標(biāo)資源。 - 銷毀:
destroy
方法在過(guò)濾器銷毀之前被調(diào)用,用于釋放資源。這也是只運(yùn)行一次的方法,通常用于清理操作。
- 初始化:
- 過(guò)濾器的使用場(chǎng)景
- 身份驗(yàn)證:用于檢查用戶是否已經(jīng)登錄,確保只有合法用戶才能訪問(wèn)受保護(hù)的資源。
- 日志記錄:記錄每個(gè)請(qǐng)求的信息,如IP地址、訪問(wèn)時(shí)間等,有助于網(wǎng)站管理和安全監(jiān)控。
- 字符編碼處理:解決不同字符集之間的兼容問(wèn)題,避免出現(xiàn)亂碼。
- 輸入數(shù)據(jù)驗(yàn)證:對(duì)用戶提交的數(shù)據(jù)進(jìn)行格式和有效性驗(yàn)證,防止非法輸入導(dǎo)致的安全問(wèn)題。
- 如何配置和使用過(guò)濾器
- 注解方式:通過(guò)
@WebFilter
注解直接在過(guò)濾器類上指定攔截路徑,這種方法更簡(jiǎn)單、直觀。 - XML配置:在
web.xml
文件中配置<filter>
和<filter-mapping>
元素,以定義過(guò)濾器及其應(yīng)用場(chǎng)景。
- 注解方式:通過(guò)
綜上所述,過(guò)濾器在Web應(yīng)用中起著至關(guān)重要的作用,從請(qǐng)求預(yù)處理到響應(yīng)后處理,都能有效地提高應(yīng)用的安全性、可用性和用戶體驗(yàn)。對(duì)于開(kāi)發(fā)人員而言,掌握過(guò)濾器的使用是提升Web應(yīng)用質(zhì)量的重要手段。
為什么我們需要過(guò)濾器
過(guò)濾器在Web開(kāi)發(fā)中扮演著至關(guān)重要的角色,它們用于增強(qiáng)、控制和修改HTTP請(qǐng)求和響應(yīng)的處理。具體如下:
- 為什么需要過(guò)濾器
- 提高代碼重用性:通過(guò)使用過(guò)濾器可以避免在多個(gè)Servlet或JSP頁(yè)面中重復(fù)編寫(xiě)相同的代碼,從而簡(jiǎn)化了代碼維護(hù)并減少了冗余。
- 實(shí)現(xiàn)全局功能:過(guò)濾器可以全局處理所有請(qǐng)求,從而統(tǒng)一實(shí)現(xiàn)如字符編碼處理、敏感詞匯過(guò)濾等功能。
- 增強(qiáng)安全性:通過(guò)身份驗(yàn)證和授權(quán),過(guò)濾器能夠阻止未經(jīng)授權(quán)的用戶訪問(wèn)受保護(hù)的資源,提升應(yīng)用的安全性。
- 提供更好的用戶體驗(yàn):例如,過(guò)濾器可以用于網(wǎng)站的統(tǒng)一登錄功能,用戶只需一次登錄即可訪問(wèn)所有受保護(hù)的資源,而無(wú)需多次認(rèn)證。
- 過(guò)濾器的工作原理及生命周期
- 工作流程:當(dāng)一個(gè)請(qǐng)求到達(dá)服務(wù)器時(shí),它會(huì)首先通過(guò)過(guò)濾器鏈,每個(gè)過(guò)濾器都有機(jī)會(huì)對(duì)請(qǐng)求進(jìn)行處理。如果請(qǐng)求通過(guò)了所有過(guò)濾器的檢驗(yàn),它最終會(huì)到達(dá)目標(biāo)資源,如servlet或JSP頁(yè)面。
- 請(qǐng)求處理:在過(guò)濾器中可以通過(guò)修改請(qǐng)求對(duì)象來(lái)實(shí)現(xiàn)對(duì)請(qǐng)求數(shù)據(jù)的預(yù)處理,例如,對(duì)輸入數(shù)據(jù)進(jìn)行解碼或驗(yàn)證。
- 響應(yīng)處理:在放行請(qǐng)求后,還可以在過(guò)濾器中修改返回給用戶的響應(yīng)數(shù)據(jù),例如,對(duì)輸出數(shù)據(jù)進(jìn)行編碼或壓縮。
- 生命周期方法:過(guò)濾器的生命周期包括
init
、doFilter
和destroy
三個(gè)方法。init
方法在過(guò)濾器創(chuàng)建時(shí)調(diào)用,用于初始化操作;doFilter
方法在實(shí)際請(qǐng)求處理中被調(diào)用;destroy
方法在過(guò)濾器銷毀前調(diào)用,用于釋放資源。
- 過(guò)濾器的主要應(yīng)用場(chǎng)景
- 字符編碼處理:通過(guò)過(guò)濾器可以統(tǒng)一設(shè)置請(qǐng)求和響應(yīng)的字符編碼,避免出現(xiàn)亂碼問(wèn)題。這在處理不同語(yǔ)言環(huán)境的應(yīng)用中尤其重要。
- 權(quán)限驗(yàn)證:過(guò)濾器可以檢查用戶是否已經(jīng)登錄或是否有權(quán)訪問(wèn)某個(gè)資源。這樣,只有合法用戶才能訪問(wèn)受保護(hù)的資源。
- 日志記錄:過(guò)濾器可以記錄每個(gè)請(qǐng)求的信息,如IP地址、訪問(wèn)時(shí)間等,有助于網(wǎng)站管理和安全監(jiān)控。
- 輸入數(shù)據(jù)驗(yàn)證:過(guò)濾器可以對(duì)用戶提交的數(shù)據(jù)進(jìn)行格式和有效性驗(yàn)證,防止非法輸入導(dǎo)致的安全問(wèn)題。
- 敏感信息過(guò)濾:過(guò)濾器可以過(guò)濾掉請(qǐng)求和響應(yīng)中的敏感信息,如SQL注入攻擊的字符串,確保數(shù)據(jù)傳輸?shù)陌踩?/li>
- 如何在Spring Boot中使用過(guò)濾器
- 創(chuàng)建過(guò)濾器類:實(shí)現(xiàn)
javax.servlet.Filter
接口,并覆蓋doFilter
方法以定義過(guò)濾器的邏輯。在doFilter
方法中,可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行處理,并通過(guò)調(diào)用FilterChain.doFilter()
將請(qǐng)求傳遞給下一個(gè)過(guò)濾器或目標(biāo)Servlet。 - 注冊(cè)過(guò)濾器:使用
@WebFilter
注解將過(guò)濾器類標(biāo)記為過(guò)濾器,并通過(guò)urlPatterns
屬性指定要攔截的URL模式。如果不使用@WebFilter
注解,也可以通過(guò)在Spring Boot配置類中使用FilterRegistrationBean
來(lái)注冊(cè)過(guò)濾器。先創(chuàng)建一個(gè)FilterRegistrationBean
實(shí)例,然后設(shè)置過(guò)濾器實(shí)例,添加URL模式,并設(shè)置優(yōu)先級(jí)。
- 創(chuàng)建過(guò)濾器類:實(shí)現(xiàn)
- 過(guò)濾器與攔截器的區(qū)別
- 處理范圍:過(guò)濾器主要作用于Servlet容器層次,可以攔截所有到達(dá)Web應(yīng)用的請(qǐng)求;而攔截器則作用于Spring MVC框架內(nèi),只能攔截由DispatcherServlet管理的請(qǐng)求。
- 執(zhí)行順序:過(guò)濾器的執(zhí)行順序依賴于它們?cè)?code>web.xml中的配置順序或通過(guò)
@WebFilter
注解聲明的順序;攔截器的執(zhí)行順序則依賴于在Spring配置文件中的聲明順序。 - 功能實(shí)現(xiàn):過(guò)濾器更側(cè)重于請(qǐng)求的預(yù)處理和響應(yīng)的后處理,比如字符編碼處理、文件上傳處理等;攔截器則更側(cè)重于視圖渲染前的控制器執(zhí)行流程,如權(quán)限檢查、表單驗(yàn)證等。
綜上所述,過(guò)濾器在Web應(yīng)用的開(kāi)發(fā)中起著至關(guān)重要的作用。從請(qǐng)求預(yù)處理到響應(yīng)后處理,過(guò)濾器都能有效地提高應(yīng)用的安全性、可用性和用戶體驗(yàn)。對(duì)于開(kāi)發(fā)人員而言,掌握過(guò)濾器的使用是提升Web應(yīng)用質(zhì)量的重要手段。
創(chuàng)建過(guò)濾器
要在Spring Boot中創(chuàng)建過(guò)濾器,您可以實(shí)現(xiàn)javax.servlet.Filter
接口或擴(kuò)展OncePerRequestFilter
類。OncePerRequestFilter
確保您的過(guò)濾器每個(gè)請(qǐng)求只執(zhí)行一次。
@RestController @RequestMapping("/customer") public class CustomerController { @GetMapping public String getMessage(){ return "welcome to filter session"; } } @Component @Slf4j public class MessageFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { log.info("[MessageFilter] - Inside doFilter method"); log.info("Local Port : " + request.getLocalPort()); log.info("Server Name : " + request.getServerName()); HttpServletRequest httpServletRequest = (HttpServletRequest) request; log.info("Method Name : " + httpServletRequest.getMethod()); log.info("Request URI : " + httpServletRequest.getRequestURI()); log.info("Servlet Path : " + httpServletRequest.getServletPath()); chain.doFilter(request, response); } }
過(guò)濾器的順序
過(guò)濾器的應(yīng)用順序可以通過(guò)@Order
注解或
@Component @Slf4j @Order(2) public class MessageFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { log.info("[MessageFilter] - Inside doFilter method"); log.info("Local Port : " + request.getLocalPort()); log.info("Server Name : " + request.getServerName()); HttpServletRequest httpServletRequest = (HttpServletRequest) request; log.info("Method Name : " + httpServletRequest.getMethod()); log.info("Request URI : " + httpServletRequest.getRequestURI()); log.info("Servlet Path : " + httpServletRequest.getServletPath()); chain.doFilter(request, response); } } @Component @Slf4j @Order(1) public class ProductFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { log.info("[ProductFilter] - Inside doFilter method"); log.info("Local Port : " + request.getLocalPort()); log.info("Server Name : " + request.getServerName()); HttpServletRequest httpServletRequest = (HttpServletRequest) request; log.info("Method Name : " + httpServletRequest.getMethod()); log.info("Request URI : " + httpServletRequest.getRequestURI()); log.info("Servlet Path : " + httpServletRequest.getServletPath()); chain.doFilter(request, response); } }
注意:如果您只想打印MessageFilter并跳過(guò)ProductFilter
要在訪問(wèn)/customer API時(shí)僅顯示MessageFilter的日志,您需要有條件地繞過(guò)ProductFilter對(duì)/customer端點(diǎn)的過(guò)濾。您可以通過(guò)修改ProductFilter來(lái)跳過(guò)特定端點(diǎn)的過(guò)濾來(lái)實(shí)現(xiàn)這一點(diǎn)。
@Component @Slf4j @Order(1) // 確保此過(guò)濾器在MessageFilter之前執(zhí)行 public class ProductFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; String requestURI = httpServletRequest.getRequestURI(); if ("/customer".equals(requestURI)) { chain.doFilter(request, response); return; // 跳過(guò)過(guò)濾器的其余部分 } log.info("[ProductFilter] - Inside doFilter method"); log.info("Local Port : " + request.getLocalPort()); log.info("Server Name : " + request.getServerName()); log.info("Method Name : " + httpServletRequest.getMethod()); log.info("Request URI : " + requestURI); log.info("Servlet Path : " + httpServletRequest.getServletPath()); chain.doFilter(request, response); } }
注冊(cè)過(guò)濾器
@Configuration public class FilterConfig { @Bean public FilterRegistrationBean<MessageFilter> loggingFilter(){ FilterRegistrationBean<MessageFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new MessageFilter()); registrationBean.addUrlPatterns("/customer/*"); // 如果需要,指定URL模式 return registrationBean; } }
OncePerRequestFilter
public class ProductFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { log.info("[ProductFilter] - Inside doFilter method"); log.info("Local Port : " + request.getLocalPort()); log.info("Server Name : " + request.getServerName()); HttpServletRequest httpServletRequest = (HttpServletRequest) request; log.info("Method Name : " + httpServletRequest.getMethod()); log.info("Request URI : " + httpServletRequest.getRequestURI()); log.info("Servlet Path : " + httpServletRequest.getServletPath()); filterChain.doFilter(request, response); } }
簡(jiǎn)單案例
1、建一個(gè)新的Java類,并實(shí)現(xiàn)javax.servlet.Filter
接口。例如,創(chuàng)建一個(gè)名為MyFilter
的類
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化代碼(可選) } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; // 在這里可以對(duì)請(qǐng)求進(jìn)行預(yù)處理操作,例如檢查請(qǐng)求頭、參數(shù)等 // 繼續(xù)執(zhí)行過(guò)濾器鏈中的下一個(gè)過(guò)濾器或目標(biāo)資源 chain.doFilter(request, response); // 在這里可以對(duì)響應(yīng)進(jìn)行后處理操作,例如修改響應(yīng)頭、內(nèi)容等 } @Override public void destroy() { // 銷毀代碼(可選) } }
2、在Spring Boot應(yīng)用程序的配置類上添加@Configuration
注解,并在該類中定義一個(gè)方法,該方法返回一個(gè)帶有@Bean
注解的FilterRegistrationBean
實(shí)例。這將注冊(cè)你的過(guò)濾器并將其添加到過(guò)濾器鏈中。
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<MyFilter> myFilter() { FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new MyFilter()); registrationBean.addUrlPatterns("/api/*"); // 設(shè)置過(guò)濾器應(yīng)用于哪些URL模式 registrationBean.setOrder(1); // 設(shè)置過(guò)濾器的順序(數(shù)字越小,優(yōu)先級(jí)越高) return registrationBean; } }
在上面的示例中,我們?yōu)镸yFilter設(shè)置了URL模式/api/*,這意味著它將應(yīng)用于所有以/api/開(kāi)頭的請(qǐng)求。你可以根據(jù)需要調(diào)整URL模式。
3、現(xiàn)在,每當(dāng)有匹配的請(qǐng)求到達(dá)時(shí),MyFilter就會(huì)被調(diào)用,你可以在doFilter方法中編寫(xiě)自定義的請(qǐng)求過(guò)濾和預(yù)處理邏輯。同樣地,你也可以在響應(yīng)返回給客戶端之前對(duì)其進(jìn)行后處理。
這樣,你就成功地在Spring Boot應(yīng)用程序中實(shí)現(xiàn)了請(qǐng)求過(guò)濾和預(yù)處理功能。記得根據(jù)你的需求調(diào)整過(guò)濾器的邏輯和配置。
以上就是SpringBoot使用Filters實(shí)現(xiàn)請(qǐng)求過(guò)濾和預(yù)處理的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Filters過(guò)濾和預(yù)處理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot項(xiàng)目部署時(shí)application.yml文件的加載優(yōu)先級(jí)和啟動(dòng)腳本問(wèn)題
Spring Boot在啟動(dòng)時(shí)會(huì)根據(jù)一定的優(yōu)先級(jí)順序加載配置文件,優(yōu)先級(jí)從高到低依次是:命令行參數(shù)、Jar包外部config目錄下的配置文件、Jar包同級(jí)目錄下的配置文件、classpath下的/config目錄、classpath根路徑2024-09-09springboot的http.server.requests服務(wù)請(qǐng)求流程源碼
這篇文章主要為大家介紹了springboot的http.server.requests服務(wù)請(qǐng)求流程源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Idea2023配置JavaWeb項(xiàng)目(最新)
本文將介紹如何配置JavaWeb項(xiàng)目,以在Idea中實(shí)現(xiàn)開(kāi)發(fā)環(huán)境,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09四個(gè)實(shí)例超詳細(xì)講解Java?貪心和枚舉的特點(diǎn)與使用
貪心算法是指,在對(duì)問(wèn)題求解時(shí),總是做出在當(dāng)前看來(lái)是最好的選擇。也就是說(shuō),不從整體最優(yōu)上加以考慮,他所做出的是在某種意義上的局部最優(yōu)解,枚舉法的本質(zhì)就是從所有候選答案中去搜索正確的解,枚舉算法簡(jiǎn)單粗暴,他暴力的枚舉所有可能,盡可能地嘗試所有的方法2022-04-04Java中ArrayList和LinkedList的區(qū)別
ArrayList和LinkedList在這個(gè)方法上存在一定的性能差異,本文就介紹了Java中ArrayList和LinkedList的區(qū)別,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06