java web过滤器filter

Posted 天际星痕

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java web过滤器filter相关的知识,希望对你有一定的参考价值。

一、过滤器简介

  过滤器filter依赖于servlet容器

  所谓过滤器顾名思义是用来过滤的,Java的过滤器能够为我们提供系统级别的过滤,也就是说,能过滤所有的web请求,
这一点,是拦截器无法做到的。在Java Web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或
者struts的action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者struts
的action前统一设置字符集,或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话)。filter 流程是线性的,url传来之后,检查之后,
可保持原来的流程继续向下执行,被下一个filter, servlet接收。

二、过滤器的作用

Filter 可实现的功能

  1)  用户授权的Filter: Filter 负责检查用户请求,根据请求过滤用户非法请求。 

  2)  日志Filter: 详细记录某些特殊的用户请求。 

  3)  负责解码的Filter: 包括对非标准编码的请求解码。 

  4)  能改变XML 内容的XSLTFilter 等。 

Filter 实现以上功能的途径

  1)在HttpServletRequest 到达Servlet 之前,拦截客户的HttpServletRequest 。 

  2)根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据。 

  3)在HttpServletResponse 到达客户端之前,拦截HttpServletResponse 。 

  4)根据需要检查HttpServletResponse ,可以修改HttpServletResponse 头和数据。

三、过滤器实现方式

SpringMVC框架是一个成熟的优秀java web开发框架,学习研究框架设计有助于我们更好的理解和掌握spring MVC,设计和写出更符合的结构和代码。

本节主要是研读SpringMVC框架中的过滤器设置,以编码处理过滤器为例来学习框架内是怎样设置过滤器的。

如上所示的spring-web.jar包结构所示, Spring的web包中中提供有很多过滤器,这些过滤器位于org.springframework.web.filter并且理所当然地实现了javax.servlet.Filter,

不过实现的方式有以下几类

        (1) 直接实现Filter,这一类过滤器只有CompositeFilter;

        (2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;

        (3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;

        (4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。

过滤器放在容器结构的什么位置?

过滤器放在web资源之前,可以在请求抵达它所应用的web资源(可以是一个Servlet、一个Jsp页面,甚至是一个html页面)之前截获进入的请求,并且在它返回到客户之前截获输出请求。Filter:用来拦截请求,处于客户端与被请求资源之间,目的是重用代码。Filter链,在web.xml中哪个先配置,哪个就先调用。在filter中也可以配置一些初始化参数。

Java中的Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应。 主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链。 

 

创建一个Filter 只需两个步骤
(1)创建Filter 处理类: 

(2)在web.xml 文件中配置Filter 。

创建Filter 必须实现javax.servlet.Filter 接口,在该接口中定义了三个方法。 
• void init(FilterConfig config): 用于完成Filter 的初始化。 
• void destroy(): 用于Filter 销毁前,完成某些资源的回收。 
• void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 实现过滤功能,该方法就是对每个请求及响应增加的额外处理。 

过滤器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驱动。

参照编码过滤器示例来查看怎么实现的

复制代码
<!-- 编码处理过滤器 -->

   <filter>

      <filter-name>encodingFilter</filter-name>

      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

      <init-param>

         <param-name>encoding</param-name>

         <param-value>utf-8</param-value>

      </init-param>

      <init-param>

         <param-name>forceEncoding</param-name>

         <param-value>true</param-value>

      </init-param>

    </filter>   

    <filter-mapping>

       <filter-name>encodingFilter</filter-name>

       <url-pattern>*.do</url-pattern>

    </filter-mapping>
复制代码

其中,filter-class 为过滤器Filter类,init-prama为注入的set参数

Filter-mapping中的url-pattern为过滤的url类型

类的继承关系

CharacterEncodingFilter r类继承了 OncePerRequestFilter 

public class CharacterEncodingFilter extends OncePerRequestFilter

 

OncePerRequestFilter 类又继承了GenericFilterBean 

public abstract class OncePerRequestFilter extends GenericFilterBean 

public abstract class GenericFilterBean implements

       Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean

设置编码的核心代码为

复制代码
@Override

   protected void doFilterInternal(

         HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)

         throws ServletException, IOException {

 

      if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {

         request.setCharacterEncoding(this.encoding);

         if (this.forceEncoding) {

            response.setCharacterEncoding(this.encoding);

         }

      }

      filterChain.doFilter(request, response);

   }
复制代码

其中filterChain为过滤器链,表示执行完这个过滤器之后接着执行下一个过滤器

区别

         我们在使用过滤器时,通常没必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防碍我们了解这几个类,就上文所述,AbstractRequestLoggingFilter继承自OncePerRequestFilter,OncePerRequestFilter继承自GenericFilterBean,所以我们知道,genericFilterBean是任何类型的过滤器的一个比较方便的超类,这个类主要实现的就是从web.xml文件中取得init-param中设定的值,然后对Filter进行初始化(当然,其子类可以覆盖init方法)。

         OncePerRequestFilter继承自GenericFilterBean,那么它自然知道怎么去获取配置文件中的属性及其值,所以其重点不在于取值,而在于确保在接收到一个request后,每个filter只执行一次,它的子类只需要关注Filter的具体实现即doFilterInternal。

        AbstractRequestLoggingFilter是对OncePerRequestFilter的扩展,它除了遗传了其父类及祖先类的所有功能外,还在doFilterInternal中决定了在过滤之前和之后执行的事件,它的子类关注的是beforeRequest和afterRequest。

       总体来说,这三个类分别执行了Filter的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类。

自定义过滤器

参数名称相同,设置set方法可以自动初始化 

复制代码
package com.my.dm.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.filter.OncePerRequestFilter;

public class TestFilter extends OncePerRequestFilter{
    
    private Logger logger =LogManager.getLogger(TestFilter.class);
    private String demo;   
    
    

    /**
     * @return the demo
     */
    public String getDemo() {
        return demo;
    }

    /**
     * @param demo the demo to set
     */
    public void setDemo(String demo) {
        this.demo = demo;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        // 请求的uri
        
        String url = request.getRequestURI();
        
        String hoString = request.getRemoteHost();
        
        String ip  = getIPAddress(request);
        logger.info("url : " + url);
        logger.info("ip : " + ip);    
        
        logger.info("demo : " + demo);    
        
        // 将请求转发到目的地 
        filterChain.doFilter(request, response);        
    }
      
     public void destroy() {
       
     }
     
     public void init(){
         
     }
    
    
    //获取真实ip
    public static String getIPAddress(HttpServletRequest request) {
        String ip = null;

        //X-Forwarded-For:Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");
    if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP:apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }
    if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP:weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }
    if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP:有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }
    if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP:nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }

        //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        //还是不能获取到,最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
    }

}
复制代码

 

复制代码
<!-- 获取登陆者信息 -->
    <filter>
        <filter-name>testFilter</filter-name>
        <filter-class>com.my.dm.filter.TestFilter</filter-class>
    
        <init-param>
            <param-name>demo</param-name>
            <param-value>112.2.36</param-value>
        </init-param>
        </filter>
    <filter-mapping>
        <filter-name>testFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
复制代码
[21:31:48:620] [INFO] - url : /dm/device/toUploadFile - com.my.dm.filter.TestFilter.doFilterInternal(TestFilter.java:46)
[21:31:48:641] [INFO] - ip : 127.0.0.1 - com.my.dm.filter.TestFilter.doFilterInternal(TestFilter.java:47)
[21:31:48:641] [INFO] - demo : 112.2.36 - com.my.dm.filter.TestFilter.doFilterInternal(TestFilter.java:49) 

主要参考自:

  https://www.cnblogs.com/lukelook/p/11079113.html

  https://www.jianshu.com/p/82ae825b849b

以上是关于java web过滤器filter的主要内容,如果未能解决你的问题,请参考以下文章

Java三大器之过滤器(Filter)的工作原理和代码演示

通过Java Web Filter实现一个最简单的UrlRewrite过滤器

java web项目中,如何通过filter获取当前action的名字?请给出具体的代码

Java-Filter过滤器用于过滤整个项目的编码

java web中filter过滤器的使用

java---servlet与filter的联系与区别