过滤器Filter
一、Filer简介
Filter过滤是作用于客户端和服务端资源文件(servlet、jsp、html等)之间的一道过滤网,可以对不符合规则的客户端请求和服务端响应进行拦截和修改。
二、应用场景
过滤器一般用于登录权限验证、资源访问权限控制、敏感词汇过滤、字符编码转换等等操作,便于代码重用,不必每个servlet中还要进行相应的操作。
三、执行流程
过滤链代码执行流程如上图:客户端从浏览器发起请求,request先进入第一个Filter进行过滤,符合条件则进入下一个Filter进行过滤。过滤器在链中的顺序与它在web.xml中配置的顺序有关,配置在前的则位于链的前端。当请求通过了链中所有过滤器后就可以访问资源文件了,如果不能通过,则可能在中间某个过滤器中被处理掉。在doFilter()方法中,chain.doFilter()前的一般是对request执行的过滤操作,chain.doFilter后面的代码一般是对response执行的操作。
四、Filter过滤器实现
SpringBoot 摒弃了繁琐的 xml 配置的同时,提供了几种注册组件:ServletRegistrationBean,
FilterRegistrationBean,ServletListenerRegistrationBean,DelegatingFilterProxyRegistrationBean,用于注册自对应的组件,如过滤器,监听器等,下面分别介绍xml方式和springboot的配置类方式。
(一)普通javaweb项目
- 实现Filter接口
使用案例:
/**
* 判断用户是否登录,未登录则退出系统
*/
public class SessionFilter implements Filter {
// 过滤配置对象
public FilterConfig config;
// 初始化
public void init(FilterConfig filterConfig) throws ServletException {
config = filterConfig;
}
// 过滤操作
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest hrequest = (HttpServletRequest)request;
HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper((HttpServletResponse) response);
// 登录登陆页面
// 过滤资源后缀参数
// 没有登陆转向页面
// 过滤器是否有效
String logonStrings = config.getInitParameter("logonStrings");
String includeStrings = config.getInitParameter("includeStrings");
String redirectPath = hrequest.getContextPath() + config.getInitParameter("redirectPath");
String disabletestfilter = config.getInitParameter("disabletestfilter");
// 过滤无效
if (disabletestfilter.toUpperCase().equals("Y")) {
chain.doFilter(request, response);
return;
}
String[] logonList = logonStrings.split(";");
String[] includeList = includeStrings.split(";");
// 只对指定过滤参数后缀进行过滤
if (!this.isContains(hrequest.getRequestURI(), includeList)) {
chain.doFilter(request, response);
return;
}
// 对登录页面不进行过滤
if (this.isContains(hrequest.getRequestURI(), logonList)) {
chain.doFilter(request, response);
return;
}
//判断用户是否登录
String user = ( String ) hrequest.getSession().getAttribute("useronly");
if (user == null) {
wrapper.sendRedirect(redirectPath);
return;
}else {
chain.doFilter(request, response);
return;
}
}
// 销毁
public void destroy() {
this.config = null;
}
/*
* 判断路径
*/
public static boolean isContains(String container, String[] regx) {
boolean result = false;
for (int i = 0; i < regx.length; i++) {
if (container.indexOf(regx[i]) != -1) {
return true;
}
}
return result;
}
}
- 在web.xml中对编写的filter类进行注册FilterConig对象,并设置它所能拦截的资源。
这里要谨记一条原则:在web.xml中,监听器>过滤器>servlet。也就是说web.xml中监听器配置在过滤器之前,过滤器配置在servlet之前,否则会出错。
<filter>
<filter-name>loginFilter</filter-name>//过滤器名称
<filter-class>com.ygj.control.loginFilter</filter-class>//过滤器类的包路径
<init—param> //可选
<param—name>参数名</param-name>//过滤器初始化参数
<param-value>参数值</param-value>
</init—pamm>
</filter>
<filter-mapping>//过滤器映射
<filter-name>loginFilter</filter-name>
<url—pattern>指定过滤器作用的对象</url-pattern>
web.xml标签介绍:
<filter>指定一个过滤器。
<filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
<filter-class>元素用于指定过滤器的完整的限定类名。
<init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。
在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
<filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字
<url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
<servlet-name>指定过滤器所拦截的Servlet名称。
<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher>子元素用来指定 Filter 对资源的多种调用方式进行拦截。
<dispatcher>子元素可以设置的值及其意义
REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
使用案例:
<filter>
<filter-name>SessionFilter</filter-name>
<filter-class>com.action.login.SessionFilter</filter-class>
<init-param>
<param-name>logonStrings</param-name><!-- 对登录页面不进行过滤 -->
<param-value>/project/index.jsp;login.do</param-value>
</init-param>
<init-param>
<param-name>includeStrings</param-name><!-- 只对指定过滤参数后缀进行过滤 -->
<param-value>.do;.jsp</param-value>
</init-param>
<init-param>
<param-name>redirectPath</param-name><!-- 未通过跳转到登录界面 -->
<param-value>/index.jsp</param-value>
</init-param>
<init-param>
<param-name>disabletestfilter</param-name><!-- Y:过滤无效 -->
<param-value>N</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(二)springboot项目
springboot项目与传统javaweb项目不同的是没有了web.xml文件。
- 实现Filter接口,同javaweb项目一致。
- FilterRegistrationBean 对象注册到spirng容器中代替web.xml配置。
使用案例:
@Configuration
public class XssConfig {
/**
* 注册XSS 过滤器
*
* @return
*/
@Bean
public FilterRegistrationBean xssFilterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new XssFilter());
filterRegistrationBean.setOrder(1); // filter order ,set it first
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addUrlPatterns("/*"); //set filter all url mapping
//配置无需过滤的路径或者静态资源,如:css,imgage等
StringBuffer excludedUriStr = new StringBuffer();
excludedUriStr.append("/oauth/token");
excludedUriStr.append(",");
excludedUriStr.append("/login");
Map<String, String> initParameters = Maps.newHashMap();
initParameters.put("excludes", excludedUriStr.toString()); // white list url
initParameters.put("isIncludeRichText", "true"); // enable or disable
filterRegistrationBean.setInitParameters(initParameters);
return filterRegistrationBean;
}
}
五、Filter过滤器原理
过滤器工厂:ApplicationFilterFactory
用于创建和缓存过滤器和创建过滤器链的工厂。
ApplicationDispatcher.invoke -》 ApplicationFilterFactory.createFilterChain
过滤器实现过程:
- 实现Filter接口,filter生命周期:
// 由web容器调用,将该过滤器将其放入服务中。servlet容器在实例化过滤器之后只调用init方法一次。init方法必须成功完成,然后才会调用执行过滤器的doFilter.
public default void init(FilterConfig filterConfig) throws ServletException {}
// 传入客户端发起的request请求和返回前端的response响应,Filter的doFilter方法由servlet容器调用,传递给此方法的FilterChain允许Filter将请求和响应传递给链中的下一个实体。
// 重写该方法,写入我们自己需要的过滤逻辑,对request和response进行处理。
// 应用场景:
// 1. 检查请求
// 2。可以选择用自定义实现包装请求对象,以过滤输入过滤的内容或头
// 3。可以选择用自定义实现包装响应对象,以过滤输出过滤的内容或头。
// 4.1)使用FilterChain对象调用链中的下一个实体(chain. dofilter()),
// 4.2)或不将请求/响应对传递给过滤器链中的下一个实体以阻止请求处理在调用过滤器链中的下一个实体之后,直接在响应上设置报头.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
// 由web容器调用,以指过滤器它正在退出服务。只有当过滤器的doFilter方法中的所有线程都退出或超时时间过后,才会调用此方法。在web容器调用这个方法之后,它将不会在过滤器的这个实例上再次调用doFilter方法.
// 此方法使过滤器有机会清理所持有的任何资源(例如,内存、文件句柄、线程),并确保任何持久状态与过滤器在内存中的当前状态同步。默认实现是NO-OP。
public default void destroy() {}
eption;
// 由web容器调用,以指过滤器它正在退出服务。只有当过滤器的doFilter方法中的所有线程都退出或超时时间过后,才会调用此方法。在web容器调用这个方法之后,它将不会在过滤器的这个实例上再次调用doFilter方法.
// 此方法使过滤器有机会清理所持有的任何资源(例如,内存、文件句柄、线程),并确保任何持久状态与过滤器在内存中的当前状态同步。默认实现是NO-OP。
public default void destroy() {}