zoukankan      html  css  js  c++  java
  • 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 }

    运行结果如下:

    完工!

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

  • 相关阅读:
    网站测试自动化系统—在测试代码中硬编码测试数据
    在WPF里面显示DIB图片格式的图片
    网站测试自动化系统—系统应该有的功能
    Windbg 教程调试非托管程序的基本命令下
    Windbg教程调试非托管程序的基本命令上
    网站测试自动化系统—基于Selenium和VSTT
    WPF中一个通用的BoolToXXX转换类
    使用MPLex实现语法高亮显示的功能代码解释
    网站测试自动化系统—数据驱动测试
    如何控制float类型小数点后的位数
  • 原文地址:https://www.cnblogs.com/liuyuhangCastle/p/9568325.html
Copyright © 2011-2022 走看看