Spring boot中使用servlet filter


Spring boot中使用servlet filter

liuyuhang原創,未經允許請勿轉載!

 

在web項目中經常需要一些場景,如參數過濾防止sql注入,防止頁面攻擊,空參數矯正等,

也可以做成token驗證,session驗證,點擊率統計等。

為了這種業務,經常會需要寫過濾器(Filter)。

servlet提供的默認過濾器比較好用,配置也還算方便;

轉入springboot開發后,注解也並不復雜,原理依舊。

 

使用filter的步驟並不復雜,主要分為幾個步驟:

  1、新建class 實現Filter抽象類(javax.servlet.Filter)

  2、override三個方法(init,doFilter,destroy)

  3、編寫ParameterRequestWrapper類繼承HttpServletRequestWrapper類(稍后說明原因)

  4、ParameterRequestWrapper類構造器

  5、構造器中復寫父類構造器並將request.getParameterMap加入子類成員變量

  6、編寫addParam方法留用

  7、修改參數並調用ParameterRequestWrapper實例保存params

  8、調用doFilter方法中的FilterChain變量,將修改后的request重新封裝

  9、在springboot入口方法中添加注解@ServletComponentScan注冊filter

其中1、2步驟為必須的,其余的步驟為修改request參數使用的封裝方式。

 

1、新建class實現Filter抽象類(javax.servlet.Filter);

2、override三個方法(init,doFilter,destroy);代碼如下:

 1 package lyh.java.filterAndInteceptor;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.Filter;
 6 import javax.servlet.FilterChain;
 7 import javax.servlet.FilterConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 import javax.servlet.annotation.WebFilter;
12 
13 import org.springframework.core.annotation.Order;
14 
15 @Order(1)//多個filter的時候,該序號越小,越早執行
16 @WebFilter(filterName = "FirstFilter", urlPatterns = "/*")//url過濾配置,並非包配置
17 public class MyMvcFilter implements Filter{
18 
19     @Override
20     public void init(FilterConfig filterConfig) throws ServletException {
21         //這里寫init邏輯,該init將在服務器啟動時調用
22     }
23 
24     @Override
25     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
26         //request處理邏輯
27         //request在封裝邏輯
28         //chain重新寫回request和response
29     }
30 
31     @Override
32     public void destroy() {
33         //這里寫destroy邏輯,該destroy邏輯將在服務器關閉時調用
34     }
35 }

 

9、在springboot入口方法中添加注解@ServletComponentScan注冊filter,代碼如下:

 1 package lyh.java;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.SpringBootConfiguration;
 5 import org.springframework.boot.web.servlet.ServletComponentScan;
 6 
 7 import lyh.java.config.DataSourceConfig;
 8 
 9 @SpringBootConfiguration//springboot基礎配置注解
10 @ServletComponentScan//springboot servlet filter
11 public class Run {
12 
13     public static void main(String[] args) throws Exception {
14         SpringApplication.run(Run.class, args);
15     }
16 }

 

到這里,就完成了Filter的簡單創建了,可以自行測試使用

 

創建Filter,一般目的是為了過濾一些參數,如果是為了中斷該訪問,應該是創建Interceptor

對於java中的mvc,過濾或者修改參數,針對的主要是request中的parameterMap的修改。

當然還有很多好用的操作,這里不做討論了。

 

修改request實例,思路上有兩個步驟是必須要考慮的:

①如何獲取request中的parameterMap並且修改?

②如何將修改后的parameterMap重新傳遞回去,使得controller能夠不受影響?

 

對於如何解決以上兩個問題,思路上也要經過兩個步驟:

①常用的是request.getParameter或request.getParamerterMap方法,但是ServletRequest是一個接口;

該接口定義了getParameterMap抽象方法,只要找到該接口的實現類,繼承該類,利用java繼承原理,

相互繼承的類,調用父類方法的時候,若子類復寫了該方法,則默認調用子類方法。

以此來實現request的重新封裝。

②在子類中定義add方法,來添加新的param,同時該param與request中原有的param進行校對,去重,

保證子類的覆蓋父類的,這樣獲取param的時候,不會有所察覺。

 

在一番查找之后,發現了一個類,來專門處理request對象的,截圖如下

注意類注解:Methods default to calling through to the wrapped request object.意為request默認調用該封裝對象

注意截圖中的構造器,該構造器傳入servletRequest對象,因此該帶參構造器肯定要被子類重寫

 

於是,編寫一個類,繼承該類,同時修改構造器,復寫getParameter方法,提供一個map作為request內容的緩存;

使得復寫的getParameter能夠從此緩存中獲取param即可,代碼如下,注釋完善,不再贅述:

 1 package lyh.java.tools;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 import javax.servlet.http.HttpServletRequest;
 7 import javax.servlet.http.HttpServletRequestWrapper;
 8 
 9 /**
10  * 重寫ParameterRequestWrapper繼承HttpServletRequestWrapper並重寫部分方法
11  * 構造器調用父類構造器
12  * 重寫param作為緩存
13  * 重寫getParameter獲取子類緩存
14  * 增加兩個add方法修改該緩存
15  * @author Liuyuhang
16  *
17  */
18 public class ParameterRequestWrapper extends HttpServletRequestWrapper {
19     
20     /**
21      * 初始化params,作為getParameter方法獲取的參數列表,內首先包含req中的paramMap,再修改
22      */
23     private Map<String , String[]> params = new HashMap<String, String[]>();
24      
25     /**
26      * 重載構造器,並調用父類構造器,將參數寫入子類params
27      * @param request
28      */
29     public ParameterRequestWrapper(HttpServletRequest request) {
30         super(request);// 將request交給父類,以便於調用對應方法的時候,將其輸出,其實父親類的實現方式和第一種new的方式類似
31         this.params.putAll(request.getParameterMap());//將參數表,賦予給當前的Map以便於持有request中的參數
32     }
33 
34     /**
35      * 重寫getParameter,代表參數從當前類中的map獲取
36      */
37     @Override
38     public String getParameter(String name) {
39         String[]values = params.get(name);
40         if(values == null || values.length == 0) {
41             return null;
42         }
43         return values[0];
44     }
45     
46     /**
47      * 獲取子類param緩存的方法
48      */
49     public Map<String,String[]> getParamsMap(){
50         return this.params;
51     }
52     /**
53      * 寫入一個map的方法
54      * @param map
55      */
56     public void addParamsMap(Map<String , Object> map) {
57         for(Map.Entry<String , Object>entry : map.entrySet()) {
58             addParam(entry.getKey() , entry.getValue());
59         }
60     }
61     /**
62      * 寫入一個參數的方法
63      * @param name
64      * @param obj
65      */
66     public void addParam(String name , Object obj) {
67         if(obj != null) {
68             if(obj instanceof String[]) {
69                 params.put(name , (String[])obj);
70             }else if(obj instanceof String) {
71                 params.put(name , new String[] {(String)obj});
72             }else {
73                 params.put(name , new String[] {String.valueOf(obj)});
74             }
75         }
76     }
77 
78 }

 

 返回Filter類中的doFilter方法,對該方法進行加工。思路是:

①封裝ServletRequest實例為子類抽象HttpServletRequest對象實例,調用剛剛寫的方法的構造器

將ParameterRequestWrapper實例化得實例對象rpw;

②獲取該prw的param,一一過濾,如字符串trim,修改null或者空字符串,修改時間日期格式,

獲取token來進行身份驗證之類。

③調用doFilter中的FilterChain參數,將重新寫過的request和response寫回。

 

具體代碼如下:

 1     @Override
 2     public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
 3         //將request封裝為子類
 4         HttpServletRequest httpReq = (HttpServletRequest) req;
 5         //調用復寫的構造器重新封裝request
 6         ParameterRequestWrapper prw = new ParameterRequestWrapper(httpReq);
 7         //這里寫requtst過濾內容的邏輯,如字符串trim,修改null或者空字符串,修改時間日期格式,獲取token來進行身份驗證之類。
 8         prw.addParam("user", "Filter-user");
 9         //調用chain執行filter並寫回參數
10         chain.doFilter(prw, resp);
11     }

 

寫個類測試下。代碼如下:

 1 package lyh.java.controller;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 import org.springframework.web.bind.annotation.RestController;
 7 
 8 @RestController//restFull Controller注解,返回json格式
 9 public class hello {
10     
11     @RequestMapping("/hello")
12     public String helloTest(HttpServletRequest req) throws Exception {
13         String user = req.getParameter("user");
14         System.out.println("user:"+user);
15         return "hello world!  "+user;
16     }
17     
18     
19 }

運行結果如下:

完工!

以上!✧(∗≧ꇴ≦)人(≧ꈊ≦∗)✧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM