1.过滤器(Filter)是什么
Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。对请求进行合法性的验证!
访问的请求和响应进行拦截。
例如: 地铁安检、身份验证。
2.过滤器快速入门
1.定义一个类,实现javax.servlet.Filter接口,实现方法
package filter;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;
public class FilterDemo implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化中...");
System.out.println("过滤器初始化中...");
System.out.println("过滤器初始化中...");
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("过滤器成功拦截了用户请求的资源...");
System.out.println("过滤器成功拦截了用户请求的资源...");
System.out.println("过滤器成功拦截了用户请求的资源...");
// 对请求的资源进行放行,如果不放行,服务器将不响应任何内容(空白页面)。
chain.doFilter(request, response);
}
public void destroy() {
System.out.println("过滤器被销毁了...");
System.out.println("过滤器被销毁了...");
System.out.println("过滤器被销毁了...");
}
}
2.在web.xml中配置相关的配置
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"><display-name>hei15_web23_filter</display-name>
<!-- 注册过滤器 -->
<filter>
<filter-name>FilterDemo</filter-name>
<filter-class>filter.FilterDemo</filter-class>
</filter>
<!-- 对哪些资源进行拦截 -->
<filter-mapping>
<filter-name>FilterDemo</filter-name>
<!-- /*表示拦截所有路径,后面再细讲 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
3.过滤器的执行顺序
Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
- 调用目标资源之前,让一段代码执行。
- 是否调用目标资源(即是否让用户访问web资源)。
- 调用目标资源之后,让一段代码执行。
web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对 象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方 法,即web资源就会被访问,否则web资源不会被访问。
4.过滤器的生命周期(了解)
生命周期:一个对象从创建到销毁的过程。
和过滤器的生命周期相关的3个方法:
- init:服务器在启动的时候创建Filter对象,并调用初始化过滤器的方法,对过滤器进行初始化
- doFilter:过滤器的核心方法,在这个方法中可以对请求对象和响应对象进行一些预处理(对请求和响应对象进行增强)。
- destroy:服务器关闭时销毁,只有在正常关闭时,才执行destroy方法。
5.过滤器配置详解
5.1.doFilter方法
这个方法,是完成过滤的核心处理方法,有3个参数
参数1:ServletRequest 请求,使用时,一般需要向下转型HttpServletRequest req = (HttpServletRequest) request;
参数2:ServletResponse 响应,使用时,也需要向下转型HttpServletResponse resp = (HttpServletResponse) response;
参数3:FilterChain 这个对象就是用来决定是否放行的对象,放行时,调用该对象的doFilter(req, resp);
放行的方法:
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;
public class FilterDemo implements Filter {
// 初始化
public void init(FilterConfig arg0) throws ServletException {
System.out.println("过滤器被创建了。。。");
}
// 核心方法:拦截
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("您访问的资源被拦截了。。。");
String username = request.getParameter("username");
if ( "zhangsan".equals(username) ) {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("跟我来一下...");
} else {
System.out.println("合法公民,请进。。。");
chain.doFilter(request, response);
}
}
// 销毁
public void destroy() {
System.out.println("过滤器被销毁了。。。");
}
}
5.2.过滤规则url-pattern
该节点用于规定拦截哪些资源,规则和Servlet中的url-pattern是一致的,有3种规则:
1.全路径匹配:
以/开始 ,例如:/index.jsp
2.通配符匹配:
a.以/开始 以/结尾 例如:/aaa/ 项目中aaa目录下的所有资源
b. /* 拦截所有的请求资源 是过滤器的最常用配置
3.后缀名(扩展名)匹配:
*.xxx,例如*.do *.abc等等,不能以/开始
5.3.转发器dispatcher(了解)
<filter-mapping><filter-name>FilterDemo</filter-name>
<!-- 配置对哪些路径进行过滤,和Servlet中的一样! -->
<url-pattern>/*</url-pattern>
<!-- 默认配置,只拦截来自客户端的请求,在服务器的内部使用转发时,
不会对转发的目标资源进行拦截 -->
<dispatcher>REQUEST</dispatcher>
<!-- 在服务器的内部做转发的时候,也被拦截 -->
<!-- <dispatcher>FORWARD</dispatcher> -->
<!-- 当使用动态引入的时候,引入的资源也会被拦截
静态引入(include指令):<%@ include file="..."%>
先合并,再翻译
动态引入(jsp动作):<jsp:include path=".." />
被引入的jsp先单独翻译,然后把最终输出的结果引入到页面中
-->
<!-- <dispatcher>INCLUDE</dispatcher> -->
<!-- 当跳转到错误页面的时候进行拦截
在web.xml中可以配置项目的全局的错误页面,
一旦服务器内部发生异常,就会跳转到配置的对应的错误页面
-->
<!-- <dispatcher>ERROR</dispatcher> -->
</filter-mapping>
项目的统一错误页面配置:
6.过滤器链
6.1.过滤器链概念
通常客户端对服务器请求之后,服务器调用Servlet之前会执行一组过滤器(多个过滤器),那么这组过滤器就称为一条过滤器链。
每个过滤器实现某个特定的功能,一个过滤器检测多个Servlet。(匹配几个,检测几个)。
一组过滤器中的执行顺序与<filter-mapping>的配置顺序呢有关。
当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源.
6.2.过滤器链示例
ResponseFilter.java
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {CustomResponseWrapper customResponseWrapper = new CustomResponseWrapper((HttpServletResponse) servletResponse);
System.out.println("ResponseFilter 执行前");
filterChain.doFilter(servletRequest,customResponseWrapper);//执行下一层过滤
System.out.println("ResponseFilter 执行后");
}
}
SecondFilter.java
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("Second doFilter执行前");
filterChain.doFilter(servletRequest,servletResponse);//这是最后一层过滤器,会直接请求resource
System.out.println("Second doFilter执行后");
}
web.xml (两个过滤器使用了相同的匹配模式‘/*’,所以会处于同一过滤链中)
<filter><filter-name>ResponseFilter</filter-name>
<filter-class>filter.ResponseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ResponseFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>SecondFilter</filter-name>
<filter-class>filter.SecondFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecondFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 执行结果:
从执行结果并结合以上分析可以很清楚的看出doFilter的执行顺序
7.全站乱码处理
在每个servlet中,都需要进行乱码的处理,这样太麻烦,可以使用过滤器来对乱码进行统一的处理
思路:在放行之前,对request进行编码的设置,那么在请求到达servlet之后,就相当于在每一个servlet中对乱码进行了处理!
定义处理乱码的过滤器
public class EncodingFilter implements Filter {// 在过滤器的核心方法中,对请求和响应对象进行预处理
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 将ServletRequest、ServletResponse转换成和Http协议相关的对象
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 解决POST的请求方式乱码问题
req.setCharacterEncoding("UTF-8");
// 处理响应的乱码
resp.setContentType("text/html;charset=UTF-8");
// 对request和response对象处理之后,放行
chain.doFilter(req, resp);
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
测试页面:
<body><form action="regServlet" method="post">
<input name="name" /><br/>
<input type="submit" value="提交" />
</form>
</body>
获取数据的Servlet
package web;import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
("all")
public class RegServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
response.getWriter().write(name);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
页面效果:
8.强制登录
系统的某些页面需要登录才能访问,如果在每个页面中都写判断,很麻烦,而且不好维护。
可以使用过滤器对需要登录才能访问的页面进行拦截,然后在过滤器中判断用户是否登录,如果没有登录,则强制跳转到登录页面:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
Object user = request.getSession().getAttribute("loginUser");
if ( user == null ) {
//response.sendRedirect(request.getContextPath() + "/login.jsp");
String path = request.getContextPath();
// top是js中的一个对象,它永远指最顶层窗口的window对象
response.getWriter().write("<script>top.location='"+path+"/login.jsp';</script>");
return ;
}
chain.doFilter(request, response);
}
public void init(FilterConfig arg0) throws ServletException {
}
public void destroy() {
}
}
配置web.xml
<filter><filter-name>PrivilegeFilter</filter-name>
<filter-class>filter.PrivilegeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PrivilegeFilter</filter-name>
<!-- 配置需要登录才能访问的资源 -->
<servlet-name>QueryGoodsPage</servlet-name> <!-- 对某个Servlet进行拦截 -->
<url-pattern>/aaaa/*</url-pattern> <!-- 对/aaaa下面的资源进行拦截 -->
<url-pattern>/bbbb/*</url-pattern> <!-- 对/bbbb下面的资源进行拦截-->
</filter-mapping>