
- 起源时间
- 20世纪初
- 作 用
- 过滤杂质等
- 分 类
- 重力、真空和加压等
- 中文名
- 过滤器
- 外文名
- filter
- 类 型
- 纤维、Y型、盘式等
-
java过滤器Filter
2019-07-31 19:08:31Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断如是否有权限访问页面等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它...一、简介
Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断如是否有权限访问页面等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应 (Request、Response)统一设置编码,简化操作;同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。它是随你的 web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁,以下通过代码示例来了解它 的使用。
二、实例
package test.filter; import ...; /** * 介绍过滤器的使用,以设置编码为例 */ public class MyFilter implements Filter { private FilterConfig config = null; private boolean isFilter = false; public void destroy() { System.out.println("MyFilter准备销毁..."); } public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException { // 强制类型转换 HttpServletRequest request = (HttpServletRequest)arg0; HttpServletResponse response = (HttpServletResponse)arg1; // 获取web.xm设置的编码集,设置到Request、Response 中 request.setCharacterEncoding(config.getInitParameter("charset")); response.setContentType(config.getInitParameter("contentType")); response.setCharacterEncoding(config.getInitParameter("charset")); // 将请求转发到目的地继续执行 chain.doFilter(request, response); } public void init(FilterConfig arg0) throws ServletException { this.config = arg0; if(isFilter){ System.out.println("MyFilter初始化..."); } } private void setIsFilter(boolean isFilter){ this.isFilter = isFilter; } }
然后在web. xml中配置该过滤器:
<filter> <filter-name>MyFilter</filter-name> <filter-class>test.filter.MyFilter</filter-class> <init-param> <param-name>isFilter</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <!-- 没有配置dispatcher就是默认request方式的 --> <dispatcher>FORWARD</dispatcher> <dispatcher>ERROR</dispatcher> <dispatcher>INCLUDE</dispatcher> </filt
三、详细介绍
在doFilter方法中通常都做些什么呢,下面列举一下:
1、通过控制对chain.doFilter的方法的调用,来决定是否需要访问目标资源。
比如,可以在用户权限验证等等。判断用户是否有访问某些资源的权限,有权限放行,没权限不执行chain.doFilter方法。
2、在调用chain.doFilter方法之前,做些处理来达到某些目的。
比如,解决中文乱码的问题等等。可以在doFilter方法前,执行设置请求编码与响应的编码。甚至可以对request接口进行封装装饰来处理get请求方式的中文乱码问题(重写相应的request.getParameter方法)。
3、在调用chain.doFilter方法之后,做些处理来达到某些目的。
比如对整个web网站进行压缩。在调用chain.doFilter方法之前用类A对response对象进行封装装饰,重写getOutputStream和重写getWriter方法。在类A内部中,将输出内容缓存进ByteArrayOutputStream流中,然后在chain.doFilter方法执行后,获取类A中ByteArrayOutputStream流缓存数据,用GZIPOutputStream流进行压缩下。Filter不仅可以通过url-pattern来指定拦截哪些url匹配的资源。而且还可以通过servlet-name来指定拦截哪个指定的servlet(专门为某个servlet服务了,servlet-name对应Servlet的相关配置)。
filter-mapping标签中dispatcher指定过滤器所拦截的资源被Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
REQUEST:
当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问或ERROR情况时,那么该过滤器就不会被调用。
INCLUDE:
如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD:
如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR:
如若在A.jsp页面page指令中指定了error属性=examError.jsp,那么A.jsp中若出现了异常,会跳转到examError.jsp中处理。而在跳转到examError.jsp时,若过滤器配置了ERROR的dispather那么则会拦截,否则不会拦截。
四、高级配置(允许代理注入spring bean)
web.xml中配置过滤器DelegatingFilterProxy:
<filter> <filter-name>permission</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>permission</filter-name> <url-pattern>*.htm</url-pattern> </filter-mapping>
在spring bean配置中加入:
<bean id="permission" class="你的bean"></bean>
bean的id必须和filter-name一样。如果想不一样,可以这样配置:
<filter> <filter-name>permission</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>targetBeanName</param-name> <param-value>test</param-value> </init-param> </filter> <filter-mapping> <filter-name>permission</filter-name> <url-pattern>*.htm</url-pattern> </filter-mapping>
在spring bean配置中加入:
<bean id="test" class="你的bean"></bean>
以上你的spring bean必须实现Filter接口。
那这样子做是为了什么呢?
答:这样做就可以将DelegatingFilterProxy所代理的filter作为spring的bean,受到spring的管理,也就是通过Spring容器来管理filter的生命周期,还有就是如果filter中需要一些Spring容器的实例,可以通过spring直接注入,另外读取一些配置文件这些便利的操作都可以通过Spring来配置实现。
其中如果设置"targetFilterLifecycle"为True,则Filter.init()和Filter.destroy()有效;若为false,则这两个方法失效。
如果大家有用到shiro(一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理等)的话,通常就会用到这个DelegatingFilterProxy了!
-
过滤器
2018-05-06 14:46:09过滤器Java Web的三大组件Servlet中组件一共有三种:Servlet、过滤器、监听器 组件 作用 实现接口 Servlet 是一个运行在服务器端的Java小程序,用来接收请求并做出响应 javax.servlet.Servlet 过滤器 用于...过滤器
Java Web的三大组件
Servlet中组件一共有三种:Servlet、过滤器、监听器
组件 作用 实现接口 Servlet 是一个运行在服务器端的Java小程序,用来接收请求并做出响应 javax.servlet.Servlet 过滤器 用于拦截用户的请求和响应,并且修改请求中的数据,对数据进行处理。 javax.servlet.Filter 监听器 监听Web程序在运行过程中对作用域操作的事件,并且对事件进行处理 多个不同的接口 javax.servlet.XxxListener 过滤器的概念
过滤器是服务器与客户端请求与响应的中间层组件,在实际项目开发中过滤器主要用于对浏览器的请求进行过滤处理,将过滤后的请求再转给下一个资源。与其他的WEB应用程序组件不同的是,过滤器是采用了“链”的方式进行处理的。
过滤器的使用场景:
- 对用户登录权限进行拦截
- 实现一些日志记录的功能
- 集中处理处理一些公共的功能,如:汉字编码和解码
过滤器的执行特点:
与Servlet的执行不同,Servlet是有访问的地址。不是由用户主动调用,而是自动执行,是通过匹配用户的访问地址去进行过滤。
过滤器编写步骤:
开发过滤器的步骤:
- 编写一个类,实现javax.servlet.Filter接口
- 实现接口中所有的方法,其中doFilter()就是执行过滤任务
- 过滤器需要在web.xml中进行配置,配置与Servlet类似。
- 示例:创建一个过滤器HelloFilter,在运行HelloServlet前和后分别输出一句话,在HelloServlet中也输出一句话,观察控制台的运行效果。HelloServlet代表Web资源
/** * a) 编写一个类,实现javax.servlet.Filter接口 * @author NewBoy * */ public class HelloFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } /** * 执行过滤任务 */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("请求经过过滤器"); //放行,让请求继续到达web资源(Servlet),调用chain中的方法 chain.doFilter(request, response); System.out.println("响应回来经过过滤器"); } @Override public void destroy() { } }
Servlet
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("我是HelloServlet,Web资源"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }
配置文件
<!-- 配置过滤器 --> <filter> <!-- 过滤器的名字 --> <filter-name>hello</filter-name> <!-- 实现了Filter接口的类全名 --> <filter-class>com.filter.HelloFilter</filter-class> </filter> <filter-mapping> <!-- 过滤器的名字,与上面的一样 --> <filter-name>hello</filter-name> <!-- 要过滤的路径 --> <url-pattern>/*</url-pattern> </filter-mapping>
过滤器的执行流程
- 用户发送请求,如果请求的地址匹配过滤器的url-pattern,则执行过滤器
- 执行过滤器中的doFilter方法,由chain.doFilter方法对请求进行放行
- 到达Web资源,响应回来的时候会再次经过过滤,执行响应的代码。
过滤器的生命周期:
过滤器加载的时机:
Servlet是用户第1次访问的时候实例化,并且初始化。
过滤器是在Web服务器启动加载当前项目完毕以后自动实例化
生命周期的方法:
Filter接口中的方法
- void init(FilterConfig filterConfig)- 在初始化过滤器的时候**执行1次**
- void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- 参数:请求,响应,过滤链- 请求和响应这两个接口是HttpServletRequest和HttpServletResponse的父接口 - 执行过滤的功能,**每次请求都会执行这个方法**
- public void destroy()
- 在服务器关闭的时候销毁,执行1次
过滤器的映射路径
Filter与Servlet中的区别:
在Servlet中url-pattern是访问的地址
在Filter中url-pattern是要过滤的地址
问:浏览器访问目标资源的路径,如果目标地址不存在,过滤器会不会运行?
如果Web资源不存在,只要匹配过滤的地址,同样会执行过滤器。
在Filter中URL的过滤方式
匹配方式 匹配哪些资源 示例 完全匹配 必须与访问地址精确匹配 /demo1
、/aaa/bbb
目录匹配 匹配某一个目录下所有的Web资源 /aaa/*
、/*
匹配所有的资源扩展名匹配 *.扩展名 匹配某一类扩展名 *.do
、*.action
有关匹配的要点:
- 以/开头的匹配模式和以扩展名结尾的配置方式,同时出现会怎样?
同时出现会在web项目加载的时候就失败,导致当前项目所有的web资源都无法访问。
过滤多个地址的写法:
一个filter-mapping中包含多个url-pattern
<filter> <filter-name>life</filter-name> <filter-class>com.filter.Demo1LifeCycleFilter</filter-class> </filter> <filter-mapping> <filter-name>life</filter-name> <url-pattern>/demo1</url-pattern> <url-pattern>/demo2</url-pattern> </filter-mapping>
一个filter对应多个filter-mapping
<filter> <filter-name>life</filter-name> <filter-class>com.filter.Demo1LifeCycleFilter</filter-class> </filter> <filter-mapping> <filter-name>life</filter-name> <url-pattern>/demo1</url-pattern> </filter-mapping> <filter-mapping> <filter-name>life</filter-name> <url-pattern>/demo2</url-pattern> </filter-mapping>
过滤Servlet的写法:
<filter> <filter-name>life</filter-name> <filter-class>com.filter.Demo1LifeCycleFilter</filter-class> </filter> <filter-mapping> <filter-name>life</filter-name> <!-- 指定Servlet的名字进行过滤 --> <servlet-name>HelloServlet</servlet-name> </filter-mapping>
过滤器的拦截方式:
默认的拦截方式
过滤器的拦截方式一共有4种,
REQUEST、FORWARD、INCLUDE、ERROR默认是请求的方式:只有直接来源于浏览器的请求,才经过过滤器。
来自转发的请求被拦截
- 在index.jsp转发到HelloServlet
<jsp:forward page="/demo1"></jsp:forward> 发现没有经过过滤器,但servlet还是访问了。
- 过滤器的配置
<filter> <filter-name>life</filter-name> <filter-class>com.filter.Demo1LifeCycleFilter</filter-class> </filter> <filter-mapping> <filter-name>life</filter-name> <url-pattern>/demo1</url-pattern> <!-- 配置拦截方式: 转发的时候经过过滤器 --> <dispatcher>FORWARD</dispatcher> </filter-mapping>
- 这样子转发也经过了过滤器
同时写多个拦截方式
<filter> <filter-name>life</filter-name> <filter-class>com.filter.Demo1LifeCycleFilter</filter-class> </filter> <filter-mapping> <filter-name>life</filter-name> <!-- 指定Servlet的名字进行过滤 --> <!-- <servlet-name>HelloServlet</servlet-name> --> <url-pattern>/demo1</url-pattern> <!-- 配置拦截方式: 转发的时候经过过滤器 --> <dispatcher>FORWARD</dispatcher> <!-- 拦截方式:浏览器直接的请求经过过滤器,默认的方式 --> <dispatcher>REQUEST</dispatcher> </filter-mapping>
过滤器的拦截类型小结
过滤类型 作用 REQUEST 直接来源于浏览器的访问地址,(包含重定向) FORWARD 转发的时候经过过滤器 INCLUDE 页面包络另一个页面访问时也经过过滤器 ERROR 页面错误时经过过滤器 FilterConfig对象
- 什么是FilterConfig对象:
用于得到过滤器中的配置参数
<filter> <filter-name>Demo2ConfigFilter</filter-name> <filter-class>com.filter.Demo2ConfigFilter</filter-class> <!-- 过滤器的初始配置参数 --> <init-param> <param-name>charset</param-name> <param-value>GBK</param-value> </init-param> <init-param> <param-name>country</param-name> <param-value>China</param-value> </init-param> </filter> <filter-mapping> <filter-name>Demo2ConfigFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
FilterConfig三个方法:
方法名 功能 String getInitParameter(String name) 通过初始参数名得到参数值 Enumeration getInitParameterNames() 得到所有的初始参数名字,返回枚举类 ServletContext getServletContext() 得到上下文对象 - 在过滤器初始化init方法中把上面的配置参数输出
//得到初始的配置参数 String charset = filterConfig.getInitParameter("charset"); String country = filterConfig.getInitParameter("country"); System.out.println(charset); System.out.println(country); //得到所有的配置参数名 Enumeration<String> names = filterConfig.getInitParameterNames(); //遍历 while (names.hasMoreElements()) { //得到其中的一个名字 String name = names.nextElement(); System.out.println("名字:" + name + ", 值:" + filterConfig.getInitParameter(name)); }
案例:POST解码
- 需求:编写过滤器,过滤所有Servlet中使用POST方法提交的汉字的编码。
- 有2个Servlet,一个是LoginServlet登录,一个是RegisterServlet注册
- 有2个JSP页面,1个是login.jsp,有表单,登录名。1个register.jsp,有表单,有注册的名字。都使用POST提交用户名使用汉字提交。
- 使用过滤器,对所有的Servlet的POST方法进行过滤。
- 过滤的编码参数,通过filterConfig得到。
- 在没有使用过滤器之前,每个Servlet必须加上汉字编码:request.setCharacterEncoding(字符集); 字符集与网页的编码要一致
//过滤的编码参数,通过filterConfig得到 @Override public void init(FilterConfig config) throws ServletException { charset = config.getInitParameter("charset"); System.out.println(charset); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //设置汉字的编码, charset相当于utf-8 request.setCharacterEncoding(charset); //放行 chain.doFilter(request, response); }
过滤器配置
<!-- 编码的过滤器 --> <filter> <filter-name>EncodingFilter</filter-name> <filter-class>com.filter.EncodingFilter</filter-class> <!-- 编码的字符集 --> <init-param> <param-name>charset</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <!-- 同时过滤2个Servlet --> <url-pattern>/login</url-pattern> <url-pattern>/register</url-pattern> </filter-mapping>
过滤链
什么是过滤器链:
一次请求可以同时经过多个过滤器,每个过滤器会将请求传递给下一个过滤器,如果下一个没有过滤器了,则传递给Web资源,这多个过滤器就组成了一个过滤器链。请求的时候经过每一个过滤器,响应的时候以相反顺序再回到每一个过滤器。
过滤器的顺序是根据配置文件中配置的顺序
Filter接口:
Filter接口中的方法:过滤器接口
- 生命周期的方法
Filter接口中的方法 参数的作用 doFilter(ServletRequest request, ServletResponse response, FilterChain chain) **ServletRequest**:代表请求对象是HttpServletRequest接口的父接口,如果要使用HttpServletRequest对象,**需要对类型进行强转**。**ServletResponse**: 代表响应对象是HttpServletResponse的父接口,如果要使用HttpServletResponse对象,**需要对类型进行强转**。**FilterChain**:代表过滤链对象用于请求的放行。 FilterChain接口中的方法:过滤链接口
FilterChain接口中的方法 参数的作用
放行
doFilter(ServletRequest request, ServletResponse response) ServletRequest:代表请求对象,是HttpServletRequest接口的父接口,如果要使用HttpServletRequest对象,需要对类型进行强转。
ServletResponse: 代表响应对象,是HttpServletResponse的父接口,如果要使用HttpServletResponse对象,需要对类型进行强转。
将上一个请求传递给下一个过滤器或Web资源示例:过滤器链
- 需求:创建两个过滤器OneFilter和TwoFilter,访问ResourceServlet,每个过滤器的请求和响应各输出一句话,观察过滤器的执行过程。
第一个过滤器
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //处理请求 System.out.println("请求经过了第1个过滤器"); //放行 chain.doFilter(request, response); //处理响应 System.out.println("响应经过了第1个过滤器"); }
第二个过滤器
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //处理请求 System.out.println("请求经过了第2个过滤器"); //放行 chain.doFilter(request, response); //处理响应 System.out.println("响应经过了第2个过滤器"); }
*配置参数的顺序!!*
<!-- 配置第1个过滤器 --> <filter> <filter-name>OneFilter</filter-name> <filter-class>com.filter.OneFilter</filter-class> </filter> <filter-mapping> <filter-name>OneFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置第2个过滤器 --> <filter> <filter-name>TwoFilter</filter-name> <filter-class>com.filter.TwoFilter</filter-class> </filter> <filter-mapping> <filter-name>TwoFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
web.xml中出现的顺序,哪个配置在前面,哪个就是前面的过滤器。
案例
实现步骤:
1. 在WebRoot下创建页面 login.jsp上使用${msg},显示信息。
1. 创建LoginServlet, 判断用户名密码是否正确,如果正确,则在会话域中保存用户信息。登录成功跳转到list.jsp,登录失败则在域中写入登录失败的信息,并且跳转到login.jsp。使用过滤器解决:创建LoginAuthorityFilter
创建成员变量HashSet,用于保存所有不经过过滤器的页面
在init方法中添加登录页面和登录的Servlet到HashSet中
- 在doFilter过滤的方法中获取当前访问的路径,判断当前路径是否在集合中,如果在集合中则放行并返回
- 否则判断会话域中是否有指定的用户信息,如果有则放行,并返回
- 最后重定向到登录页面
过滤器
public class LoginAuthorityFilter implements Filter { //创建成员变量HashSet,用于保存所有不经过过滤器的页面 private HashSet<String> paths = new HashSet<String>(); @Override public void destroy() { } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { //得到子接口 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; //得到会话域 HttpSession session = request.getSession(); //在doFilter过滤的方法中获取当前访问的路径,判断当前路径是否在集合中 String path = request.getServletPath(); System.out.println(path); if (paths.contains(path)) { //如果在集合中则放行并返回 chain.doFilter(request, response); return; } //否则判断会话域中是否有指定的用户信息 String user = (String) session.getAttribute("user"); if (user != null) { //如果有则放行,并返回 chain.doFilter(request, response); return; } System.out.println("拦截到非法的用户:" + request.getRemoteAddr()); //最后重定向到登录页面 response.sendRedirect(request.getContextPath() + "/login.jsp"); } @Override public void init(FilterConfig config) throws ServletException { // 在init方法中添加登录页面和登录的Servlet到HashSet中 paths.add("/login.jsp"); paths.add("/login"); } }
servlet
HttpSession session = request.getSession(); //得到用户名和密码 String name = request.getParameter("user"); String pwd = request.getParameter("pwd"); //判断是否登录成功 if ("newboy".equals(name) && "123".equals(pwd)) { //如果登录成功,将用户的信息保存到会话域中 session.setAttribute("user", name); //跳转到list.jsp response.sendRedirect(request.getContextPath() + "/list.jsp"); } //如果登录失败,写错误信息到请求域,跳转到login.jsp显示出来 else { request.setAttribute("msg", "用户名或密码不正确"); request.getRequestDispatcher("/login.jsp").forward(request, response); }
-
Java Web之过滤器(Filter)
2018-07-31 16:58:40过滤器(Filter) 过滤器实际上就是对web资源进行拦截,做一些处理后再交给servlet。 通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理 大概流程图如下 应用场景 自动登录 统一设置...过滤器(Filter)
过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理
通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理大概流程图如下
应用场景
自动登录
统一设置编码格式
访问权限控制
敏感字符过滤等
创建Filter
在Servlet中我们一般都会对request和response中的字符集编码进行配置,如果Servlet过多字符集编码发生变化时修改起码会很麻烦,这些通用的字符集编码配置等工作我们可以放到Filter中来实现。
下面我们来创建一个处理字符集编码的Filter。右键包名—>new ---->Filter
输入过滤器名称,跟创建Servlet一样,这里我们直接使用 @WebFilter 注解,不再去web,xml中进行配置了。
创建完成后默认代码,可以看到,CharsetFilter实现了Filter接口,实现了3个方法。3个方法的作用已经在注释中写清楚了。
package filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter(filterName = "CharsetFilter") public class CharsetFilter implements Filter { public void destroy() { /*销毁时调用*/ } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { /*过滤方法 主要是对request和response进行一些处理,然后交给下一个过滤器或Servlet处理*/ chain.doFilter(req, resp);//交给下一个过滤器或servlet处理 } public void init(FilterConfig config) throws ServletException { /*初始化方法 接收一个FilterConfig类型的参数 该参数是对Filter的一些配置*/ } }
配置Filter
可配置的属性有这些
常用配置项
urlPatterns
配置要拦截的资源- 以指定资源匹配。例如
"/index.jsp"
- 以目录匹配。例如
"/servlet/*"
- 以后缀名匹配,例如
"*.jsp"
- 通配符,拦截所有web资源。
"/*"
**initParams **
配置初始化参数,跟Servlet配置一样例如
initParams = { @WebInitParam(name = "key",value = "value") }
dispatcherTypes **
配置拦截的类型,可配置多个。默认为DispatcherType.REQUEST**
例如dispatcherTypes = {DispatcherType.ASYNC,DispatcherType.ERROR}
其中DispatcherType是个枚举类型,有下面几个值
FORWARD,//转发的 INCLUDE,//包含在页面的 REQUEST,//请求的 ASYNC,//异步的 ERROR;//出错的
下面我们来对CharsetFilter 代码进行一下修改
package filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebInitParam; import java.io.IOException; @WebFilter(filterName = "CharsetFilter", urlPatterns = "/*",/*通配符(*)表示对所有的web资源进行拦截*/ initParams = { @WebInitParam(name = "charset", value = "utf-8")/*这里可以放一些初始化的参数*/ }) public class CharsetFilter implements Filter { private String filterName; private String charset; public void destroy() { /*销毁时调用*/ System.out.println(filterName + "销毁"); } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { /*过滤方法 主要是对request和response进行一些处理,然后交给下一个过滤器或Servlet处理*/ System.out.println(filterName + "doFilter()"); req.setCharacterEncoding(charset); resp.setCharacterEncoding(charset); chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException { /*初始化方法 接收一个FilterConfig类型的参数 该参数是对Filter的一些配置*/ filterName = config.getFilterName(); charset = config.getInitParameter("charset"); System.out.println("过滤器名称:" + filterName); System.out.println("字符集编码:" + charset); } }
这样一个简单的字符集编码处理的过滤器就完成了
我们看看执行打印的结果
需要注意的是
过滤器是在服务器启动时就会创建的,只会创建一个实例,常驻内存,也就是说服务器一启动就会执行Filter的init(FilterConfig config)方法.
当Filter被移除或服务器正常关闭时,会执行destroy方法
多个Filter的执行顺序
在我们的请求到达Servle之间是可以经过多个Filter的,一般来说,建议Filter之间不要有关联,各自处理各自的逻辑即可。这样,我们也无需关心执行顺序问题。
如果一定要确保执行顺序,就要对配置进行修改了,执行顺序如下- 在web.xml中,filter执行顺序跟
<filter-mapping>
的顺序有关,先声明的先执行 - 使用注解配置的话,filter的执行顺序跟名称的字母顺序有关,例如AFilter会比BFilter先执行
- 如果既有在web.xml中声明的Filter,也有通过注解配置的Filter,那么会优先执行web.xml中配置的Filter
我们写个小例子看一下
新建3个Filter,加上之前的CharsetFilter一共四个
其中CharsetFilter和ABFilter是通过注解声明的
CharsetFilter注解配置
@WebFilter(filterName = "CharsetFilter", urlPatterns = "/*",/*通配符(*)表示对所有的web资源进行拦截*/ initParams = { @WebInitParam(name = "charset", value = "utf-8")/*这里可以放一些初始化的参数*/ })
ABFilter
@WebFilter(filterName = "ABFilter",urlPatterns = "/*")
AFilter和BFilter是在web.xml配置的。
执行顺序跟<filter>
的顺序无关
<filter-mapping>
的顺序才决定执行顺序<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <filter> <filter-name>AFilter</filter-name> <filter-class>filter.AFilter</filter-class> </filter> <filter> <filter-name>BFilter</filter-name> <filter-class>filter.BFilter</filter-class> </filter> <!--这里BFilter在AFilter之前--> <filter-mapping> <filter-name>BFilter</filter-name> <url-pattern>/filter.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>AFilter</filter-name> <url-pattern>/filter.jsp</url-pattern> </filter-mapping> </web-app>
每个Filter添加了打印语句,如下
以ABFilter为例package filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter(filterName = "ABFilter",urlPatterns = "/*") public class ABFilter implements Filter { private String filterName; public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println(filterName + " doFilter()"); chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException { filterName= config.getFilterName(); System.out.println("过滤器名称:" + filterName +" init"); } }
下面我们来访问filter.jsp看看打印结果
可以看到,执行结果符合预期。
BFilter和AFilter是在web.xml中声明的,且BFilter的<filter-mapping>
在前,故BFilter在AFilter之前执行。
ABFilter和CharsetFilter是通过注解声明的,故他俩在BFilter和AFilter之后执行,但是ABFilter的名称以A开头,故在CharsetFilter之前执行
访问权限控制小例子##
下面我们写一个访问控制权限控制的小例子。
我们在浏览一些网站经常有这个情况,没有登录时是不允许我们访其主页的,只有登录过后才能访问。
下面我们就用Filter简单实现一下。需求分析
- 登录时将登录的账号密码保存到cookie中,下次访问时携带账号和密码,过滤器中进行校验
- 用户没有登录直接访问主页时,要跳转到登录页面
- 登录过滤器不对登录页面进行过滤
我们先来看一下项目结构
这里主要看一下LoginFilter的代码
我们在LoginFilter中对非登录页面的其他jsp都会进行过滤,判断cookie中是否携带了account和pwd。
如果有这两个数据表示之前登录过,那么对数据进行校验,正确的话就进行下一个操作。
否则的话,跳转到登录界面package filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter(filterName = "LoginFilter", urlPatterns = "*.jsp", dispatcherTypes = {}) public class LoginFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("LoginFilter doFilter"); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; String url = request.getRequestURI(); System.out.println("请求的url:" + url); /*登录页面不需要过滤*/ int idx = url.lastIndexOf("/"); String endWith = url.substring(idx + 1); if (!endWith.equals("login.jsp")) { /*不是登录页面 进行拦截处理*/ System.out.println("不是登录页面,进行拦截处理"); if (!isLogin(request)) { System.out.println("没有登录过或者账号密码错误,跳转到登录界面"); response.sendRedirect("login.jsp"); } else { System.out.println("已经登录,进行下一步"); chain.doFilter(req, resp); } } else { System.out.println("是登录页面,不进行拦截处理"); chain.doFilter(req, resp); } } private boolean isLogin(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); String account = ""; String pwd = ""; if (cookies != null && cookies.length > 0) { for (Cookie cookie : cookies) { if (cookie.getName().equals("account")) { account = cookie.getValue(); } else if (cookie.getName().equals("pwd")) { pwd = cookie.getValue(); } } } if (account.equals("") || pwd.equals("")) { return false; } else if (account.equals("yzq") && pwd.equals("123")) { return true; } return false; } public void init(FilterConfig config) throws ServletException { System.out.println("LoginFilter init"); } }
执行效果
可以看到,我们在没有登录的情况下直接去访问index.jsp页面时会自动跳转到登录页面,在登录成功后,再次直接访问index页面则可以访问。
下面是demo
如果你觉得本文对你有帮助,麻烦动动手指顶一下,可以帮助到更多的开发者,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!
- 以指定资源匹配。例如
-
-
拦截器(Interceptor)和过滤器(Filter)的执行顺序和区别
2018-06-03 13:31:00一、引言 本来想记录一下关于用户登陆和登陆之后的权限管理、...关于Interceptor解决权限和菜单管理的问题,在放在下一篇写吧,就酱紫。...1、过滤器(Filter) 首先说一下Filter的使用地方,我们在配置web.x...一、引言
本来想记录一下关于用户登陆和登陆之后的权限管理、菜单管理的问题,想到解决这个问题用到Interceptor,但想到了Interceptor,就想到了Filter,于是就想说一下它们的执行顺序和区别。关于Interceptor解决权限和菜单管理的问题,在放在下一篇写吧,就酱紫。
二、区别
1、过滤器(Filter)
首先说一下Filter的使用地方,我们在配置web.xml时,总会配置下面一段设置字符编码,不然会导致乱码问题:
<filter> <filter-name>encoding</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>encoding</filter-name> <servlet-name>/*</servlet-name> </filter-mapping>
配置这个地方的目的,是让所有的请求都需要进行字符编码的设置,下面来介绍一下Filter。
(1)过滤器(Filter):它依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的,是用来做一些过滤操作,获取我们想要获取的数据,比如:在Javaweb中,对传入的request、response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者Controller进行业务逻辑操作。通常用的场景是:在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。
2、拦截器(Interceptor)
拦截器的配置一般在SpringMVC的配置文件中,使用Interceptors标签,具体配置如下:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.scorpios.atcrowdfunding.web.LoginInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.scorpios.atcrowdfunding.web.AuthInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
(2)拦截器(Interceptor):它依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法,比如动态代理就是拦截器的简单实现,在调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在调用方法后打印出字符串,甚至在抛出异常的时候做业务逻辑的操作。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
三、代码
下面在一个项目中我们使用既有多个过滤器,又有多个拦截器,并观察它们的执行顺序:
(1)第一个过滤器:public class TestFilter1 implements Filter { @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //在DispatcherServlet之前执行 System.out.println("############TestFilter1 doFilterInternal executed############"); filterChain.doFilter(request, response); //在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后 System.out.println("############TestFilter1 doFilter after############"); } }
(2)第二个过滤器:
public class TestFilter2 implements Filter { @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //在DispatcherServlet之前执行 System.out.println("############TestFilter2 doFilterInternal executed############"); filterChain.doFilter(request, response); //在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后 System.out.println("############TestFilter2 doFilter after############"); } }
(3)在web.xml中注册这两个过滤器:
<!-- 自定义过滤器:testFilter1 --> <filter> <filter-name>testFilter1</filter-name> <filter-class>com.scorpios.filter.TestFilter1</filter-class> </filter> <filter-mapping> <filter-name>testFilter1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 自定义过滤器:testFilter2 --> <filter> <filter-name>testFilter2</filter-name> <filter-class>com.scorpios.filter.TestFilter2</filter-class> </filter> <filter-mapping> <filter-name>testFilter2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
再定义两个拦截器:
(4)第一个拦截器:public class BaseInterceptor implements HandlerInterceptor{ /** * 在DispatcherServlet之前执行 * */ public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("************BaseInterceptor preHandle executed**********"); return true; } /** * 在controller执行之后的DispatcherServlet之后执行 * */ public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println("************BaseInterceptor postHandle executed**********"); } /** * 在页面渲染完成返回给客户端之前执行 * */ public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("************BaseInterceptor afterCompletion executed**********"); } }
(5)第二个拦截器:
public class TestInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("************TestInterceptor preHandle executed**********"); return true; } public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println("************TestInterceptor postHandle executed**********"); } public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("************TestInterceptor afterCompletion executed**********"); } }
(6)、在SpringMVC的配置文件中,加上拦截器的配置:
<!-- 拦截器 --> <mvc:interceptors> <!-- 对所有请求都拦截,公共拦截器可以有多个 --> <bean name="baseInterceptor" class="com.scorpios.interceptor.BaseInterceptor" /> <mvc:interceptor> <!-- 对/test.html进行拦截 --> <mvc:mapping path="/test.html"/> <!-- 特定请求的拦截器只能有一个 --> <bean class="com.scorpios.interceptor.TestInterceptor" /> </mvc:interceptor> </mvc:interceptors>
(7)、定义一个Controller控制器:
package com.scorpios.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class TestController { @RequestMapping("/test") public ModelAndView handleRequest(){ System.out.println("---------TestController executed--------"); return new ModelAndView("test"); } }
(8)、测试结果:
启动测试项目,地址如下:http://www.localhost:8080/demo,可以看到控制台中输出如下:
这就说明了过滤器的运行是依赖于servlet容器,跟springmvc等框架并没有关系。并且,多个过滤器的执行顺序跟xml文件中定义的先后关系有关。接着清空控制台,并访问:http://www.localhost:8080/demo/test,再次看控制台的输出:
从这个控制台打印输出,就可以很清晰地看到有多个拦截器和过滤器存在时的整个执行顺序了。当然,对于多个拦截器它们之间的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关。四、总结
对于上述过滤器和拦截器的测试,可以得到如下结论:
(1)、Filter需要在web.xml中配置,依赖于Servlet;
(2)、Interceptor需要在SpringMVC中配置,依赖于框架;
(3)、Filter的执行顺序在Interceptor之前,具体的流程见下图;
(4)、两者的本质区别:拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。 -
-
布隆过滤器 - Redis 布隆过滤器,Guava 布隆过滤器 BloomFilter
2019-09-25 09:35:09文章目录布隆过滤器 - Redis 布隆过滤器,Guava 布隆过滤器 BloomFilter1、布隆过滤器的起源,用途2、布隆过滤器的概念3、布隆过滤器的优缺点1、优点2、缺点4、应用场景5、布隆过滤器的工作原理6、布隆过滤器的设计 ... -
wireshark显示过滤器和捕获过滤器
2018-11-14 20:52:57关于wireshark 过滤器的使用,包括wireshark显示过滤器和wireshark捕获过滤器。 -
布隆过滤器 - Redis 布隆过滤器,Guava 布隆过滤器 BloomFilter - 代码实践
2019-09-25 11:01:42文章目录布隆过滤器 - Redis 布隆过滤器,Guava 布隆过滤器 BloomFilter - 代码实践1、通过guava 实现的布隆过滤器2、通过 redisson 实现的布隆过滤器3、通过Jedis 实现的布隆过滤器 布隆过滤器 - Redis 布隆过滤器... -
Wireshark捕获过滤器和显示过滤器
2019-12-17 21:19:26wireshark中非常实用的捕获过滤器以及显示过滤器的使用技巧 -
Vue过滤器和自定义过滤器
2017-07-25 19:19:091、过滤器的用法,用 ‘|' 分割表达式和过滤器。 Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的... -
JavaWeb 过滤器:工作原理,生命周期,过滤器链,过滤器分类
2018-07-31 10:03:36过滤器的工作原理 过滤器的生命周期 过滤器链 Web项目中多个过滤器实现,多个过滤器对应同一个路近执行顺序如何? 过滤器链: 过滤器链执行过程: 过滤器分类 @WebFilter,在servlet3中,可以... -
过滤器与监听器
2019-05-09 09:58:46过滤器与监听器 本文是基于Windows 10系统环境,学习和使用基于java的过滤器与监听器: Windows 10 MyEclipse 10 一、过滤器 (1) 过滤器出现的背景 项目开发中,经常会涉及到重复代码的实现,如设置编码 涉及到... -
过滤器过滤特定的url_如何从过滤器中排除URL
2020-07-02 03:45:52默认情况下,过滤器不支持排除特定的URL模式,每当您为过滤器定义URL模式时,任何与该模式匹配的请求都将由过滤器无例外处理。 从过滤器中排除URL的最简单方法是将过滤器映射到非常特定的模式。 在早期开发阶段... -
【SSM - SpringMVC篇】09 SpringMVC拦截器Interceptor详解,多个拦截器的执行顺序,...拦截器和过滤器的区别
2020-10-16 23:34:32配置图文,超详细!!SpringMVC拦截器Interceptor详解,多个拦截器的执行顺序,拦截器进行权限判断和日志记录,拦截器和过滤器的区别 -
过滤器的过滤规则
2019-04-04 14:26:37在web.xml中配置过滤器。这里要谨记一条原则:在web.xml中,监听器>过滤器>servlet。也就是说web.xml中监听器配置在过滤器之前,过滤器配置在servlet之前,否则会出错。 在配置中需要注意的有两处:一是<... -
关于拦截器与过滤器使用场景、拦截器与过滤器的区别整理
2017-01-03 18:28:48关于拦截器与过滤器使用场景、区别整理 -
Google布隆过滤器与Redis布隆过滤器详解
2019-06-26 20:16:48一、什么是布隆过滤器? 布隆过滤器可以用来判断一个元素是否在一个集合中。它的优势是只需要占用很小的内存空间以及有着高效的查询效率。 对于布隆过滤器而言,它的本质是一个位数组:位数组就是数组的每个元素都... -
Vue学习之旅Part3:Vue的全局过滤器和私有过滤器
2020-04-18 19:24:36Vue允许自定义过滤器 可被用作一些常见的文本格式化 过滤器可以用在两个地方:mustache插值表达式 和 v-bind表达式 过滤器应被添加在JavaScript表达式的尾部 由“管道”符指示(管道符就是一个竖杠:|) 一、全局过滤... -
过滤器链、过滤器链调用原理与顺序、过滤器配置细节、过滤器过滤类型
2017-07-17 10:21:26过滤器链调用原理与顺序: web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在... -
JAVA-过滤器和拦截器1
2020-07-05 21:09:291、过滤器 (Filter) 过滤器的配置比较简单,直接实现 Filter 接口即可,也可以通过 @WebFilter 注解实现对特定 URL 拦截,看到 Filter 接口中定义了三个方法。 init() :该方法在容器启动初始化过滤器时被调用,它在... -
Filter过滤器链(多个过滤器)
2020-01-21 13:57:47当有多个过滤器的时候,执行顺序很...搞清楚了谁先执行,下面看看多个过滤器是怎么执行的,过滤器的执行是先去,把所有路过的过滤器都执行一遍,然后再回来再执行一遍所有的过滤器 package com.zhiying.filter;... -
Revit二开--过滤器之选择过滤器
2019-04-24 19:59:51Revit二开–过滤器-选择过滤器 鼠标在选择的时候通过一个过滤器就可以筛选指定类别的元素, 那么我在用鼠标点选的时候只要选择墙,怎么做呢。 案例如下: var ele = sel.PickObject(ObjectType.Element,/*要添加的...