灵魂三问:(what,why,how)
1.什么是过滤器
过滤器介绍
- Filter是sun公司中servlet2.3后增加的一个新功能
- Servlet规范中三个技术 Servlet Listener Filter
- 在JavaEE中定义了一个接口 javax.servlet.Filter来描述过滤器
- 通过Filter可以拦截访问web资源的请求与响应操作
- web开发人员通过Filter技术,对web服务器管理资源:例如Jsp,Servlet,静态图片文件或静态html文件等进行拦截,从而实现一些特殊的功能。例如**实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息、避免中文乱码【规定web资源都使用UTF-8编码】、权限验证【规定只有带有Session或Cookie的浏览器,才能访问web资源】**等等一些高级功能
也就是所:当需要限制用户访问资源时,在处理请求时提前处理某些资源、服务器响应的内容对其进行处理在返回,我们就是用过滤器完成的!
过滤器在web容器中的流程:
从图上可以发现,当浏览器的请求发送给服务器时,会遇到过滤器,将访问请求进行过滤,之后服务器才响应,web资源返回时,也会经过过滤器。
Filter API介绍
(由于自身的接触较浅,现在明白Java基础的API为jdk_API,现在使用的javaweb技术属于javaee,为java_ee_API)
Filter是javax.servlet包下的一个接口,主要有以下三个方法:
Filter的快速入门
- 创建一个类实现javax.servlet.Filter接口并实现方法
public class MyFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//chain.doFilter(request.response);
}
@Override
public void destroy() {
}
}
- 在web.xml文件中配置Filter拦截路径(拦截规则在下文中)
<!-- 配置 一个过滤器,用于拦截请求-->
<filter>
<filter-name>myfilter</filter-name>
<filter-class>com.gyf.web.filter.MyFilter</filter-class>
</filter>
<!-- 过滤器的拦截规则 -->
<filter-mapping>
<filter-name>myfilter</filter-name>
<!-- 拦截任何一个请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
- 图解过滤的拦截原理
注意:chain.doFilter(request,repsonse);是放行资源,如果不写,则被拦截的规则没有办法访问到资源
FilterChain功能介绍
FilterChain是servlet容器为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。过滤器使用FilterChain调用链中的下一个过滤器,如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。
Filter链
Filter链介绍
多个Filter对同一个资源进行了拦截,那么当我们在开始的Filter中执行chain.doFilter(request,response)时,是访问下一个Filter,直到最后一个Filter执行时,它后面没有了Filter,才会访问web资源。
多个Filter的访问顺序问题
它们的执行顺序取决于<filter-mapping>
在web.xml文件中的顺序
过滤器的生命周期
- 当服务器启动,会创建Filter对象,并调用init方法,只调用一次。
- 当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法
- 当服务器关闭时,会调用Filter的destroy方法来进行销毁操作。
FilterConfig介绍
FilterConfig功能介绍
在Filter中的init方法上有一个参数叫FilterConfig,FilterConfig是Filter的配置对象。 其作用如下:
- 获取Filter的名称
- 获取初始化参数
- 获取ServletContext对象
FilterConfig常用API
获取web.xml中配置的filter初始化参数
<filter>
<filter-name>myfilter</filter-name>
<filter-class>com.gyf.web.filter.MyFilter</filter-class>
<!-- 初始化参数 -->
<init-param>
<param-name>username</param-name>
<param-value>zhangsan</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456abcdefg</param-value>
</init-param>
</filter>
<!-- 过滤器的拦截规则 -->
<filter-mapping>
<filter-name>myfilter</filter-name>
<!-- 拦截任何一个请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
可以通过FilterConfig的方法直接获取初始化参数
Filter映射配置详情
Filter基本配置格式介绍
<filter>
<filter-name>filter 名称</filter-name>
<filter-value>filter 类全名</filter-value>
</filter>
<filter-mapping>
<filter-name>filter 名称</filter-name>
<url-pattern>映射路径</url-pattern>
</filter-mapping>
关于url-pattern配置
- 完全匹配
要求必须以“/”开始。【不能写成/*.jsp,否则就会报错,映射报错】
例:<url-pattern>
/PersonServlet</url-pattern>
会拦截PersonServlet请求。 - 目录匹配
要求必须以“/“开始,以*结束。
例:<url-pattern>
/*</url-pattern>
拦截任一请求,如Servlet请求和.jsp请求。 - 扩展名匹配
不能以“/“开始,以*.xxx结束
例:<url-pattern>
*.jsp</url-pattern>
拦截以jsp后缀的请求。
关于servlet-name配置
针对于servlet拦截的配置 配置,在Filter中它的配置项上有一个标签,它用于设置当前Filter拦截哪一个servlet。是通过servlet的name来确定的。
关于dispatcher配置
可以取的值有** REQUEST FORWARD ERROR INCLUDE**
它的作用是:当以什么方式取访问web资源时,进行拦截操作。
- REQUEST 当时从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置的 它也是默认值
- FORWARDD 它描述的是请求转发的拦截方式配置
- ERROR 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
- INCLUDE 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
2.为什么需要用到过滤器
举个栗子:
例子1 没有过滤器解决中文乱码问题
- 如果没有用到过滤器:浏览器通过http请求放数据给Servlet,如果存在中文呢,就必须指定编码,否则就会乱码!
- jsp页面提交中文数据给Servlet处理
<form action="${pageContext.request.contextPath}/Demo1" method="post">
<input type="text" name="username">
<input type="submit" value="提交">
</form>
- Servlet没有指定编码的情况下,获取得到的是乱码
POST和GET方式产生中文乱码的原因:
Servlet中解决中文乱码的问题:
- post和get请求在接收参数时都会产生乱码问题。两者存在本质的区别。
POST乱码的原因:
- Tomcat7版本,接收客户端的请求数据时以ISO-8859-1来接收的,到了Tomcat8后,接收的请求数据是UTF-8.
- 浏览器使用的是UTF-8的编码,浏览器的中文数据提交给服务器时,Tomcat8以UTF-8的编码接收数据,在Servlet中读取数据时,拿到的UTF-8的数据,此时只需将request的编码转为UTF-8,(request.setCharacterEncoding("utf-8"))乱码问题即可解决。
GET请求乱码的问题:
-利用post传参数时,点击提交按钮之后,数据会封装到Form Data中(http中多了一个上传请求头,专门用来封装post的删除),request对象可以解析到发送过来的数据,所以只用把编码设置成UTF-8就可以解决乱码问题。
- Get方式的数据时通过消息行带过去的(Servlet?后面的时get方式带过去的参数),没有封装到reqeust对象,所以使用request设置编码是无效的。
- GET方式由消息题带过去给服务器的时候则根据Tomcat的版本可能会发生区别,则先将数据反向转换得到原始的数据,然后进行UTF-8的转码。
利用new String进行转码
username = new String(usernaem.getBytes("ISO-8859-1","utf-8")
- 总结
- post方式直接改request对象的编码
- get方式需要手工转换编码,原因在于get的参数是从消息体传过去的。
tomcat7.0中get请求中文乱码解决方案:
- 在config/server配置uri解码方式
- 如果是项目开发完成了,打包成war包,放在tomcat7.0/webapps的时候,修改tomcat/conf/server.xml,在connector中添加URIEncoding=”utf-8”
- 如果是开发阶段,在Eclipse开发工具的server/tomcat7.0/server.xml配置。添加<Connector URIEncoding=”utf-8” connectionTimeout=”20000”
- 在Servlet中配置uri解码方式
把字符串转回字节码,然后在进行utf-8编码
String username = request.getParameter(“username”);
//先还原iso编码,再转成utf-8
username = new String (username.getBytes(“ISO-8859-1”),“utf-8”);
post请求中中文乱码解决:
- 设置request.setCharacterEncoding(“utf-8”);
也就是说:如果每次接受客户端过来的中文数据,在Servlet中都要设定编码。这样的代码的重复率很高!!!
有过滤器解决中文乱码问题
使用过滤器:当在过滤器中指定了编码,可以使全站的web资源都是使用该编码,并且重用性是非常理想的!
利用过滤器解决post请求的中文乱码问题:
- 创建一个MyEncodingFilter类实现Filter接口
- 配置web.xml文件
其中存在三个对象之间的关系:RequestFacade、HttpServletRequest以及ServletRequest
利用过滤器解决get请求的中文乱码问题:
利用装饰设计模式,创造了一个新的myRequest对象。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//1,设置POST请求中文乱码的问题
request.setCharacterEncoding("utf-8");
System.out.println("拦截请求:"+request);
//2.解决get请求中文乱码问题
//request : RequestFacade;
HttpServletRequest hsr =(HttpServletRequest)request;
if(hsr.getMethod().equalsIgnoreCase("get")) {
MyRequest myRequest = new MyRequest(hsr);
//放行请求
chain.doFilter(myRequest, response);
}else {
chain.doFilter(request, response);
}
}
}
/**
* Wrapper包装类,装饰设计模式,内部有个真实对象的引用
* @author Yyyz
*
*/
class MyRequest extends HttpServletRequestWrapper{
private HttpServletRequest request;
private boolean isEncoding = false;//是否已经是utf-8编码
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
return getParameterMap().get(name)[0];
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = request.getParameterMap();
if(isEncoding == true) {
return map;
}
//遍历value,改成utf-8编码
for(Entry<String,String[]> entry : map.entrySet()) {
//取数组值
String[] values = entry.getValue();
for(int i=0;i<values.length;i++) {
try {
values[i] = new String(values[i].getBytes("ISO-8859-1"),"UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
isEncoding = true;
return map;
}
}
过滤器API
只要java类实现了Filter接口就可以称为过滤器。
接口中的三个方法:init()、destory()、doFilter()。init和destory方法只有在web服务器加载和销毁时才执行,且只执行一次。
doFilter(ServletRequest,ServletResponse,FilterChain),从前面两个参数可以发现:过滤器可以完成任何协议的过滤操作!
FilterChain是一个接口,里面有定义了doFilter()方法。可以理解:过滤器不单单只有一个,在Java中使用了链式结构。把所有的过滤器都放在FilterChain里面,如果符合条件,就执行下一个过滤器(如果没有过滤器,就执行目标资源)
Filter部署
过滤器和Servlet是一样的,需要部署到web服务器上的。
第一种方式:在web.xml文件中配置
filter
<filter>
用于注册过滤器
- 用于为过滤器指定一个名字,该元素的内容不能为空。
- 元素用于指定过滤器的完整的限定类名。
- 元素用于为过滤器指定初始化参数,它的子元素指定参数的名字,指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
filter-mapping<filter-mapping>
元素用于设置一个Filter所负责拦截的资源
一个Filter拦截的资源可通过两种方式来指定:Servlet名称和资源访问的请求路径
- 子元素用于设置filter的注册名称。该值必须是在元素中声明过的过滤器的名字
- 设置 filter 所拦截的请求路径(过滤器关联的URL样式)
- 指定过滤器所拦截的Servlet名称。
- 指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
第二种方式:通过注解配置
@webFilter(filterName = "FilterDemo1",urlPatterns="/*")
上面的配置是"/*",所有的web资源都会被过滤器拦截。
过滤器的执行顺序
完整的流程是:客户端发送http请求到Web服务器上,Web服务器执行过滤器,执行到”准备放行“时,就把字符串输出到控制台上,接着执行doFilter()方法,Web服务器发现没有过滤器了,就执行目标资源(也就是test.jsp)。目标资源执行完后,回到过滤器上,继续执行代码,然后输出”放行完成“
Filter简单应用
- filter的三种典型应用:
- 可以在filter中根据条件决定是否调用chain.doFilter(request,response)方法,即是否让目标资源执行
- 在让目标资源执行之前,可以对request和response作预处理,再让目标资源执行
- 在目标资源执行之后,可以捕获目标资源的执行结果,从而实现一些特殊的功能
禁止浏览器缓存所有动态页面
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//让web资源不缓存,只需要设置http中的response的请求头即可
//我们使用的是http协议,ServletResponse并没有能够设置请求头的方法,所以要强转成HttpServlet
//一般我们写Filter都会把他俩强转成Http类型的
HttpServletRequest myrequest =(HttpServletRequest)request;
HttpServletResponse myresponse = (HttpServletResponse)response;
myresponse.setDateHeader("Expires", -1);
myresponse.setHeader("Cache-control", "no-cache");
myresponse.setHeader("pragma", "no-cache");
//放行目标资源的response以及设置成不缓存的
chain.doFilter(request, response);
}
- 没有过滤之前,响应头是这样的:
- 过滤之后,响应头是这样的:
例子2:实现自动登录(一种视频教学,一种博客)
本文为笔记整理,是视频学习和参考Java3y博主的知识整理,如有侵权,请联系删除。
本文参考:过滤器第一篇【介绍、入门、简单引用】