Tomcat常用的过滤器

摘要:
然而,事实上,Tomcat已经提供了一些相关的过滤器,可以通过简单的配置来使用。最近,在系统地学习了Tomcat架构之后,结合一些源代码记录,我们总结了最常用的过滤器。请参阅Tomcat架构分析、Tomcat官方过滤器配置I、跨域过滤器CorsFilterorg.apcache.catalina。filters CorsFilter是跨域资源共享规范的一个实现,通常用于将前端与后端、静态资源与后端等分离。max浏览器允许缓存飞行前请求结果。
前言

之前我很肤浅的以为为了实现某种请求过滤功能(比如图片转换、文件上传、安全认证等),都需要自己去实现javax.servlet.Filter。之后在web.xml中配置即可。

但事实上,Tomcat已经提供了部分相关的过滤器(本文只介绍常用的7个过滤器),只需要简单配置就可以使用。最近通过系统学习Tomcat架构之后,结合部分源码记录总结最常用的几种过滤器。

参考资料《Tomcat架构解析》(有需要PFD电子书的朋友可以评论或者私信),Tomcat官方Filter配置(Tomcat 8为例)


一、跨域过滤器CorsFilter

org.apcache.catalina.filters.CorsFilter是跨域资源共享规范的一个实现,常常用于前后端分离,静态资源与后端分离等情况。它主要在HttpServletResponse中增加Access-Control-*头,同时保护HTTP响应避免拆分,如果请求无效或者禁止访问,则返回403响应码。

配置示例

<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
  <init-param>
    <param-name>cors.allowed.origins</param-name>
    <param-value>*</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.methods</param-name>
    <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.headers</param-name>
    <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
  </init-param>
  <init-param>
    <param-name>cors.exposed.headers</param-name>
    <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
  </init-param>
  <init-param>
    <param-name>cors.support.credentials</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
    <param-name>cors.preflight.maxage</param-name>
    <param-value>10</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

参数说明

  1、cors.allowed.origins

允许访问的跨域资源列表,"*"表示允许访问来自任何域的资源,多个域用逗号分隔,默认为"*"

  2、cors.allowed.methods

可以用于访问资源的HTTP方法列表,","分隔,用于跨域请求。这些方法将出现在Prefligh(预检请求)响应头Access-Control-Allow-Methods的一部分,t默认为"GET, POST, HEAD, OPTIONS"

  3、cors.allowed.headers

构造请求时可以使用的请求头,以","分隔,这些方法将出现在Prefligh(预检请求)响应头Access-Control-Allow-Headers的一部分,默认为Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers

  4、cors.exposed.headers

浏览器允许访问的头部信息列表,","分隔。这些方法将出现在Prefligh(预检请求)响应头Access-Control-Allow-Headers的一部分,默认为空。

  5、cors.preflight.maxage

浏览器允许缓存的Preflght请求结果的时间,单位为秒。如果为负数,则表示CorsFilter不会添加头到Preflight响应,这些方法将出现在Prefligh(预检请求)响应头Access-Control-Max-Age的一部分,默认为1800.

  6、cors.support.credentials

表示资源是否支持用户证书,这些方法将出现在Prefligh(预检请求)响应头Access-Control-Allow-Credentials的一部分,默认为true

  7、cors.request.decorate

Cors规范属性是否已经添加到HttpServletRequest,默认为true。CorsFiter会为HttpServletRequest添加请求相关信息,cors.request.decorate配置为true,那么以下属性将会被添加

1)cors.isCorsRequest:用于请求是否为Cors请求。

2)cors.request.origin:源URL,请求源自的页面URL。

3)cors.request.type:Cors的请求类型,如下:

      SIMPLE:非Preflight请求为先导的请求。

ACTUAL:以Preflight请求为先导的请求。

PRE_FLIGHT:Preflight请求

NOT_CORS:正常同域请求

INVALID_CORS:无效的域请求

4)cors.request.headers:作为Preflight请求Access-Control-Request-Header头发送的请求头信息。

二、CSRF保护过滤器CsrfPreventionFilter

org.apcache.catalina.filters.CsrfPreventionFilter为Web应用提供了基本的CSRF保护。返回的客户端的所有链接均通过HttpServletResponse.encodeRedirectURL(String)与HttpServletResponse.encodeURL(String)进行编码,该过滤器生成一个随机数并存储到会话session中进行对比,URL使用该随机数进行编码。当接收到下一个请求时,请求中随机数与会话中的进行对比,只有两者相同时,请求才会被允许。

配置示例

<filter>
        <filter-name>CsrfPreventionFilter</filter-name>
        <filter-class>org.apache.catalina.filters.CsrfPreventionFilter</filter-class>
        <init-param>
            <param-name>denyStatus</param-name>
            <param-value>403</param-value>
        </init-param>
        <init-param>
            <param-name>entryPoints</param-name>
            <param-value>/html,/html/list</param-value>
        </init-param>
        <init-param>
            <param-name>nonceCacheSize</param-name>
            <param-value>5</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CsrfPreventionFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

参数说明

  1、denyStatus:HTTP响应吗,用于驳回拒绝请求,默认为403

  2、entryPoints:以","为分隔的URL列表,这些列表将不会进行随机数检测(主要用于通过导航离开受保护应用,之后再返回)

 if ("GET".equals(req.getMethod()) && this.entryPoints.contains(this.getRequestedPath(req))) {
                skipNonceCheck = true;
 }

  3、nonceCacheSize:随机数缓存大小。先前发布的随机数被缓存到一个LRU缓存中以支持并发请求,有限的用于浏览器刷新等行为(可能导致随机数不是当前的),默认为5

private int nonceCacheSize = 5;
....
if (nonceCache == null) {
    nonceCache = new CsrfPreventionFilter.LruCache(this.nonceCacheSize);
      if (session == null) {
           session = req.getSession(true);
       }
    session.setAttribute("org.apache.catalina.filters.CSRF_NONCE", nonceCache);
}

  4、randomClass:用于生成随机数的类,必须是java.util.Random实例,如不设置默认为java.security.SecureRandom

三、防止参数丢失过滤器FailedRequestFilter

org.apcache.catalina.filters.FailedRequestFilter用于触发请求的参数解析,当参数解析失败时,将会拒绝请求,该Filter用于确保客户端提交的参数信息不发生丢失。该过滤器的原理是:先调用ServletRequest.getParameter(首次调用会触发Tomcat服务器的请求参数解析,如果参数解析失败,将结果放到请求属性org.apache.catalina.parameter_parse_failed中),之后判断属性org.apache.catalina.parameter_parse_failed的值,如果不为空则直接返回400。

为了能正确解析参数,需要该Filter之前设置字符集编码过滤器SetCharacterEncodingFilter。此外,该过滤器是不支持r初始化参数的

//判断是否为有效的请求:org.apache.catalina.parameter_parse_failed为null
private booleanisGoodRequest(ServletRequest request) {
        request.getParameter("none");
        return request.getAttribute("org.apache.catalina.parameter_parse_failed") == null;
    }
四、获取客户端IP过滤器RemoteAddrFilter

org.apcache.catalina.filters.RemoteAddrFiler允许比较提交的客户端IP地址(通过ServletRequest.getRemoteAddr获取)是否符合指定正则表达式。

配置示例

    <filter>
      <filter-name>Remote Address Filter</filter-name>
      <filter-class>org.apache.catalina.filters.RemoteAddrFilter</filter-class>
      <init-param>
        <param-name>allow</param-name>
        <param-value>127.d+.d+.d+|::1|0:0:0:0:0:0:0:1</param-value>
      </init-param>
    </filter>
    <filter-mapping>
      <filter-name>Remote Address Filter</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

参数说明

1、allow:指定允许访问的客户端IP地址

2、deny:拒绝访问的客户端地址

3、denyStatus:拒绝请求时返回的HTTP响应吗。

五、获取客户端Host过滤器RemoteHostFilter

org.apcache.catalina.filters.RemoteHostFiler允许比较提交请求的客户端主机名是否符合指定的正则表达式,以确定是否允许继续处理请求。参数同RemoteAddrFilter

六、获取原始客户端IP过滤器RemoteIpFilter

当客户端通过HTTP代理或者负载均衡访问服务器时,对于服务器来说,请求直接源自前置的代理服务器,此时获取到的远程IP实际为代理服务器的IP地址。

这时候如何获得原始的客户端的IP地址呢?

HTTP协议通过X-Forwarded-For头信息记录了资客户端到应用服务器前置代理的IP地址,RemoteIpFilter通过解析该请求头,将请求中的IP地址与主机名替换为客户端真实的IP地址和主机信息,此外还可以通过X-Forwardred-Proto请求头替换当前的协议名称http/https、服务器端口及request.secure。

X-Forwarded-For的格式如下:

X-Forwarded-For: client, proxy1, proxy2

最左侧client为最原始的客户端IP,如上示例中客户端经过了proxy1、proxy2、proxy3三级代理(最后一层proxy3不显示,通过ServletRquest.getRemoteAddr获取)。在负载均衡的情况下,RemoteAddrFilter和RemoteHostFilter需要与该过滤器配合使用,否则无法正确限制访问客户端。

通常我们获取X-Forwarded-For使用如下Java代码:

 public staticString getIp(HttpServletRequest request) {
        String requestAddr = request.getHeader("x-forwarded-for");
        if (requestAddr == null || requestAddr.length() == 0 || "unknown".equalsIgnoreCase(requestAddr)) {
            requestAddr = request.getHeader("Proxy-Client-IP");
        }
        if (requestAddr == null || requestAddr.length() == 0 || "unknown".equalsIgnoreCase(requestAddr)) {
            requestAddr = request.getHeader("WL-Proxy-Client-IP");
        }
        if (requestAddr == null || requestAddr.length() == 0 || "unknown".equalsIgnoreCase(requestAddr)) {
            requestAddr =request.getRemoteAddr();
        }
        returnrequestAddr;
    }

配置示例

1)基本处理X-Forwarded-For头的配置

  <filter>
        <filter-name>RemoteIpFilter</filter-name>
        <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
      </filter>
      <filter-mapping>
        <filter-name>RemoteIpFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
      </filter-mapping>

2)处理X-Forwarded-For与x-forwarded-proto头部的配置

  <filter>
        <filter-name>RemoteIpFilter</filter-name>
        <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
        <init-param>
          <param-name>protocolHeader</param-name>
          <param-value>x-forwarded-proto</param-value>
        </init-param>
      </filter>
      <filter-mapping>
        <filter-name>RemoteIpFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
      </filter-mapping>

3)使用内部代理的高级配置

 <filter>
       <filter-name>RemoteIpFilter</filter-name>
       <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
       <init-param>
         <param-name>allowedInternalProxies</param-name>
         <param-value>192.168.0.10|192.168.0.11</param-value>
       </init-param>
       <init-param>
         <param-name>remoteIpHeader</param-name>
         <param-value>x-forwarded-for</param-value>
       </init-param>
       <init-param>
         <param-name>remoteIpProxiesHeader</param-name>
         <param-value>x-forwarded-by</param-value>
       </init-param>
       <init-param>
         <param-name>protocolHeader</param-name>
         <param-value>x-forwarded-proto</param-value>
       </init-param>
     </filter>

4)使用可信任代理高级配置

<filter>
       <filter-name>RemoteIpFilter</filter-name>
       <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
       <init-param>
         <param-name>allowedInternalProxies</param-name>
         <param-value>192.168.0.10|192.168.0.11</param-value>
       </init-param>
       <init-param>
         <param-name>remoteIpHeader</param-name>
         <param-value>x-forwarded-for</param-value>
       </init-param>
       <init-param>
         <param-name>remoteIpProxiesHeader</param-name>
         <param-value>x-forwarded-by</param-value>
       </init-param>
       <init-param>
         <param-name>trustedProxies</param-name>
         <param-value>proxy1|proxy2</param-value>
       </init-param>
     </filter>
七、字符集编码过滤器SetCharacterEncodingFilter

提供了一种设置字符集编码的方式,通常情况下默认ISO-8859-1编码,但实际生产环境推荐使用UTF-8编码,而请求中的编码可以在未指定编码时使用,也可以强制覆盖。

配置示例

<filter>
        <filter-name>SetCharacterEncodingFilter</filter-name>
        <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>ignore</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SetCharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

参数说明

  1、encoding:指定的字符集编码  

  2、ignore:表示是否忽略客户端请求设置的字符集编码,如果为true那么都会将请求字符集编码覆盖,如果为false,请求没有指定字符集编码时设置。默认为false

免责声明:文章转载自《Tomcat常用的过滤器》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇基于C#实现的单点登录Linux 中C/C++ search path(头文件搜索路径)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

c# url自动解码解决方案

昨天下午易宝维护人员联系我,询问我三笔订单的状态,他们是扣费成功了,我们这还是订单待扣费状态,经过检查发现双方的签名有问题,仔细一看这三笔订单都有一个共性,用户名都包含中文,然后就对症下药。。。 由于易宝再扣费成功后通知我方前,对字符串用gb2312 urlencode了,而我方接口是用.net实现的,所以默认的编码格式是utf-8,所以接收到请求后就把字...

MYSQL数据库基础篇

MySQL基础入门 一、数据库1 数据库概念(了解) 1.1 什么是数据库 数据库就是用来存储和管理数据的仓库! 数据库存储数据的优先: l  可存储大量数据; l  方便检索; l  保持数据的一致性、完整性; l  安全,可共享; l  通过组合分析,可产生新数据。 数据库具有原子性,数据不可再分割! 1.2 数据库的发展历程 l  没有数据库,使用...

gzip压缩算法

gzip,zlib,以及图形格式png,使用的是同一个压缩算法deflate。我们通过对gzip源码的分析来对deflate压缩算法做一个详细的说明: 第一,gzip压缩算法基本原理的说明。 第二,gzip压缩算法实现方法的说明。 第三,gzip实现源码级的说明。 1. Gzip压缩算法的原理          gzip 对于要压缩的文件,首先使用LZ7...

JS 富文本编码、解码

第一种  escape()和unescape()方法 escape() 方法能够把 ASCII之外的所有字符转换为 %xx 或 %uxxxx(x表示十六进制的数字)的转义序列。从 u000 到 u00ff 的 Unicode 字符由转义序列 %xx 替代,其他所有 Unicode 字符由 %uxxxx 序列替代。  如 var str = "编程最美";...

PHP中文乱码的常见解决方法总结

PHP中文乱码是PHP开发中的常见问题之一。PHP中文乱码有时发生在网页本身,有些产生在于MySQL交互的过程中,有时与操作系统有关。下面进行一番总结。   一.首先是PHP网页的编码   1. php文件本身的编码与网页的编码应匹配   a. 如果欲使用gb2312编码,那么php要输出头:header(“Content-Type: text/html;...

C#.NET 中的类型转换

C# 出来也有些日子了,最近由于编程的需要,对 C# 的类型转换做了一些研究,其内容涉及 C# 的装箱/拆箱/别名、数值类型间相互转换、字符的 ASCII 码和 Unicode 码、数值字符串和数值之间的转换、字符串和字符数组/字节数组之间的转换、各种数值类型和字节数组之间的转换、十六进制数输出以及日期型数据的一些转换处理,在这里与大家分享—— 1. 装箱...