【转载】Filter介绍,执行顺序,实例

Servlet Filter详解

Filter介绍,执行顺序,实例

Filter介绍

Filter可认为是Servlet的一种“变种”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。它与Servlet的区别在于:它不能直接向用户生成响应。完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

Filter有如下几个用处。

  • 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。
  • 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。
  • 在HttpServletResponse到达客户端之前,拦截HttpServletResponse。
  • 根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

Filter有如下几个种类。

  • 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。
  • 日志Filter:详细记录某些特殊的用户请求。
  • 负责解码的Filter:包括对非标准编码的请求解码。
  • 能改变XML内容的XSLT Filter等。
  • Filter可负责拦截多个请求或响应;一个请求或响应也可被多个请求拦截。

创建一个Filter只需两个步骤:

  • 建Filter处理类;
  • web.xml文件中配置Filter。

                  

         

下面先介绍一个简单的记录日志的Filter,这个Filter负责拦截所有的用户请求,并将请求的信息记录在日志中。

LogFilter过滤器类

package com.ljq.servlet;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class LogFilter implements Filter {
// FilterConfig可用于访问Filter的配置信息
private FilterConfig config;

// 实现初始化方法
public void init(FilterConfig config) {
this.config = config;
}

// 实现销毁方法
public void destroy() {
this.config = null;
}

// 执行过滤的核心方法
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// ---------下面代码用于对用户请求执行预处理---------
// 获取ServletContext对象,用于记录日志
ServletContext context = this.config.getServletContext();
long before = System.currentTimeMillis();
System.out.println("开始过滤...");
// 将请求转换成HttpServletRequest请求
HttpServletRequest hrequest = (HttpServletRequest) request;
// 记录日志
context.log("Filter已经截获到用户的请求地址: " + hrequest.getServletPath());
// Filter只是链式处理,请求依然放行到目的地址
chain.doFilter(request, response);

// ---------下面代码用于对服务器响应执行后处理---------
long after = System.currentTimeMillis();
// 记录日志
context.log("过滤结束");
// 再次记录日志
context.log("请求被定位到" + hrequest.getRequestURI() + "所花的时间为: "
+ (after - before));
}

}

        

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>com.ljq.servlet.LogFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

上面程序实现了doFilter()方法,实现该方法就可实现对用户请求进行预处理,也可实现对服务器响应进行后处理——它们的分界线为是否调用了chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。

在上面的请求Filter中,仅在日志中记录请求的URL,对所有的请求都执行chain.doFilter (request,reponse)方法,当Filter对请求过滤后,依然将请求发送到目的地址。如果需要检查权限,可以在Filter中根据用户请求 的HttpSession,判断用户权限是否足够。如果权限不够,直接调用重定向即可,无须调用chain.doFilter(request,reponse)方法。

          

          

下面是一个实例:

package com.ljq.servlet;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class AuthorityFilter implements Filter {
private FilterConfig config;

// 实现初始化方法
public void init(FilterConfig config) {
this.config = config;
}

// 实现销毁方法
public void destroy() {
this.config = null;
}

// 执行过滤的核心方法
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 获取该Filter的配置参数
String encoding = config.getInitParameter("encoding");
String login = config.getInitParameter("login");

// 设置request编码用的字符集
request.setCharacterEncoding(encoding);
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
// 获取客户请求的路径
String requestPath = req.getServletPath();


// 如果session范围的user为null,即表明没有登录
// 且用户请求的既不是登录页面,也不是处理登录的页面
if (session.getAttribute("user") == null
&& !requestPath.endsWith(login)) {
// forward到登录页面
request.setAttribute("tip", "您还没有登录");
request.getRequestDispatcher(login).forward(request, response);
}
// 放行请求
else {
chain.doFilter(request, response);
}
}
}
      

上面Filter的doFilter方法里2行斜体字代码用于获取Filter的配置参数,而程序中粗体字代码则是此Filter的核心,①号代码 按配置参数设置了request编码所用的字符集,接下来的粗体字代码判断session范围内是否有user属性——没有该属性即认为没有登录,如果既没有登录,而且请求地址也不是登录页,系统直接跳转到登录页面。

在web.xml文件中配置该Filter,使用init-param元素为该Filter配置参数,init-param可接受如下两个子元素:

param-name:指定参数名。

param-value:指定参数值。

该Filter的配置片段如下:

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 定义Filter -->
<filter>
<filter-name>authority</filter-name>
<filter-class>com.ljq.servlet.AuthorityFilter</filter-class>
<!-- 下面3个init-param元素配置了3个参数 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>login</param-name>
<param-value>/login.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>authority</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

上面配置片段中粗体字代码为该Filter指定了2个配置参数,指定login为/login.jsp,这表明:如果没有登录该应用,普通用户只能访问/login.jsp。只有当用户登录该应用后才可自由访问其他页面。

<div class = "postDesc">posted on <span id="post-date">2011-07-21 19:03</span> <a href='http://www.cnblogs.com/linjiqin/'>Ruthless</a> 阅读(<span id="post_view_count">...</span>) 评论(<span id="post_comment_count">...</span>)  <a href ="http://i.cnblogs.com/EditPosts.aspx?postid=2113180" rel="nofollow">编辑</a> <a href="#" onclick="AddToWz(2113180);return false;">收藏</a></div>

$(function () { setTimeout(function () { incrementViewCount(cb_entryId); }, 50); deliverAdT2(); deliverAdC1(); deliverAdC2(); loadNewsAndKb(); loadBlogSignature(); LoadPostInfoBlock(cb_blogId, cb_entryId, cb_blogApp, cb_blogUserGuid); GetPrevNextPost(cb_entryId, cb_blogId, cb_entryCreatedDate); loadOptUnderPost(); GetHistoryToday(cb_blogId, cb_blogApp, cb_entryCreatedDate); });

        <div id="blog-calendar" style="display:none"></div><script type="text/javascript">loadBlogDefaultCalendar();</script><br>
        <div id="blog-sidecolumn"></div><script type="text/javascript">loadBlogSideColumn();</script></DIV>


<think>我们正在讨论的是如何控制Filter执行顺序,特别是使用注解方式(如@WebFilter)时。用户的问题来源于之前关于清除请求头中token的讨论,但引用的内容转向了Filter执行顺序控制。用户询问是否可以通过参数来控制Filter执行顺序。根据引用[1][4]: -在传统的web.xml中,Filter执行顺序由<filter-mapping>的声明顺序决定,先声明的先执行。 -使用@WebFilter注解时,没有直接的属性可以指定执行顺序。引用[4]列出了@WebFilter的属性,但没有一个属性是用来控制顺序的。然而,引用[2]提到Filter执行顺序是通过Filter链中Filter的位置来控制的,而Servlet规范没有规定使用注解时的执行顺序。实际上,使用@WebFilter时,Filter执行顺序是不确定的,通常由类名的字母顺序决定(但具体实现可能因Servlet容器而异)。 那么,如何控制多个@WebFilter执行顺序呢? 解决方案: 1. **使用web.xml配置**:放弃使用@WebFilter注解,改为在web.xml中显式配置Filter按照需要的顺序声明<filter-mapping>。2.**使用@Order注解(仅适用于Spring)**:如果项目使用了Spring框架,可以通过@Order注解或实现Ordered接口来控制Filter执行顺序。注意,这仅适用于通过Spring注册的Filter(如使用@Component注解的Filter),而不适用于直接使用@WebFilterFilter。 3. **使用FilterRegistrationBean(Spring Boot)**:在Spring Boot中,可以通过FilterRegistrationBean来注册Filter设置order属性。示例(Spring Boot):```java@Configurationpublic classFilterConfig{@Beanpublic FilterRegistrationBean<UserLoginFilter>userLoginFilter(){FilterRegistrationBean<UserLoginFilter> registration=new FilterRegistrationBean<>();registration.setFilter(newUserLoginFilter());registration.addUrlPatterns("/*");registration.setOrder(1);//设置顺序,数值越小优先级越高returnregistration;}@BeanpublicFilterRegistrationBean<ApiLogFilter>apiLogFilter(){FilterRegistrationBean<ApiLogFilter>registration =new FilterRegistrationBean<>();registration.setFilter(newApiLogFilter());registration.addUrlPatterns("/*");registration.setOrder(2);//在UserLoginFilter之后执行return registration;} }``` 4. **修改类名(不推荐)**:由于一些容器按类名字母顺序加载,可以通过在类名前加字母(如A_MyFilter,B_MyFilter)来强制顺序。但这种方法不可靠且不优雅。 结论: -在纯Servlet应用中(不使用Spring),使用web.xml是控制Filter执行顺序的可靠方法。 -在Spring Boot应用中,使用FilterRegistrationBean设置order属性是推荐方法。 因此,回答用户问题:使用@WebFilter注解时,不能通过参数直接控制执行顺序。必须采用其他机制(如web.xml或Spring的排序机制)来控制顺序。 针对用户之前关于token清除的问题,如果清除token的Filter没有在添加token的Filter之前执行,那么就需要调整Filter执行顺序,确保清除操作先执行。在Spring Boot中,可以通过设置FilterRegistrationBean的order值来实现。相关问题: 1.如何在SpringBoot中自定义Filter执行顺序? 2.使用@WebFilter注解时,Filter执行顺序由什么决定?3.如何确保安全相关的Filter在其他Filter之前执行?4.在Servlet规范中,Filter执行顺序规则是什么?5.如何在非Spring的JavaWeb应用中控制Filter顺序?</think>### Filter 执行顺序的控制方式 Filter执行顺序**不能通过参数直接控制**,但可以通过以下机制间接实现精确控制: #### 1. **传统 web.xml 配置(优先级最高)** 通过 `<filter-mapping>` 的声明顺序控制执行顺序: ```xml <!-- 先声明的 Filter 优先执行 --> <filter> <filter-name>TokenRemovalFilter</filter-name> <filter-class>com.example.TokenRemovalFilter</filter-class> </filter> <filter-mapping> <filter-name>TokenRemovalFilter</filter-name> <url-pattern
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值