过滤器是一个服务器端组件可以截取用户端的请求与响应信息,并对这些信息进行过滤。
过滤器主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。使用Filter的完整流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
例如在之前的学生信息管理的例子中,如果要求只有在用户登录后才能查看主页面和其它页面,不能让用户直接在地址栏里输入url就能访问,就可以使用过滤器来实现,可以在用户登陆验证的Servlet里将用户的信息放到session中,然后用户每次发送请求后,请求会先被过滤器拦截下来进行身份验证,如果没有登录即session中获取不到用户信息那就全部转发到登录界面。而且可以同时在过滤器中设置编码转换,这样就不需要每次当请求里含有中文时都需要在提交到的Servlet中设置请求编码和响应编码问题,但我看了别人的博客这只对post请求有效,如果是get请求要使用装饰模式增强request对象来实现。
另外之前看到一个视频讲到要对所有用户能输入的地方加以小心,试了下如果输入一些HTML标签那整个页面就乱掉了,如果是其它恶意的代码那就更不安全,在GitHub上看到别人的 代码 使用过滤器完成对HTML标签的转义,同样用到了装饰模式来实现,这次就记录下过滤器的一些知识点和使用装饰模式实现HTML标签转义的过程,之后还是要好好学习下设计模式。
创建Filter类 1.创建一个类继承Filter接口,接口中定义了如下三个方法。
void init(FilterConfig fConfig):Filter的初始化,Filter在服务器启动时就创建,创建之后马上执行这个方法。用来初始化一些参数,这个方法可以读取web.xml文件中过滤器的参数。
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain):用于实现过滤的方法,该方法会在每次请求中调用,或每次响应中调用;ServletRequest 封装了请求信息,ServletResponse 封装了响应信息;当用户请求访问与过滤器关联的URL时,Web容器将先调用过滤器的doFilter方法。
FilterChain参数用于访问后续过滤器,FilterChain参数可以调用chain.doFilter方法,如果在一个目标资源上,部署了多个过滤器,doFilter() 方法表示的是执行下一个过滤器的 doFilter() 方法,如果当前过滤器是最后一个过滤器,那么调用 chain.doFilter() 方法表示执行目标资源;在chain.doFilter方法执行之前进行的处理是对请求的预处理,在chain.doFilter方法执行之后执行的处理是对响应进行的后处理。
void destroy():在服务器关闭时对Filter进行销毁,在Filter销毁之前会执行这个方法,完成某些资源的回收。
配置Filter 配置Filter同样有两种方式:
一是通过注解 通过@WebFilter()修饰一个Filter类,用于对Filter进行配置。
@WebFilter用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将会根据具体的属性配置将相应的类部署为过滤器。filterName
属性指定过滤器的name属性,等价于<filter-name>
;urlPatterns
属性指定一组过滤器的url匹配模式,value
属性等价于urlPatterns
属性,两个属性不能共存。dispatcherTypes
属性指定过滤器的转发模式。
二是在web.xml中通过配置文件配置
过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明,然后映射到你的应用程序的部署描述符中的 Servlet 名称或 URL 模式。当Web容器启动Web应用程序时,它会为你在部署描述符中声明的每一个过滤器创建一个实例。当多个过滤器过滤的URL相同时,此时多个过滤器就形成了一个过滤器链,过滤器的执行顺序会与在web.xml配置文件中的配置顺序一致,一般把Filter配置在所有的Servlet之前。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<filter >
<filter-name > AuthorityFilter</filter-name >
<filter-class > filter.AuthorityFilter</filter-class >
</filter >
<filter-mapping >
<filter-name > AuthorityFilter</filter-name >
<url-pattern > /*</url-pattern >
</filter-mapping >
过滤器的四种拦截方式
拦截直接请求方式:REQUEST (默认)
拦截请求转发方式:FORWARD
拦截请求包含方式:INCLUDE
拦截错误转发方式:ERROR
实现不同的拦截方式可以在 <filter-mapping>
中进行配置中进行不同的配置:
<dispatvher>REQUEST</dispatvher>
<dispatvher>FORWORD</dispatvher>
<dispatvher>INCLUDE</dispatvher>
<dispatvher>ERROR</dispatvher>
若在web.xml配置文件中没有写出上面四个拦截配置时默认该过滤器只拦截请求。
在代码中解决编码和登录认证问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
("/*" )
public class implements Filter {
public void destroy () {
}
public void doFilter (ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request.setCharacterEncoding("UTF-8" );
response.setCharacterEncoding("UTF-8" );
response.setContentType("text/html;charset=UTF-8" );
HttpSession session = request.getSession();
String requestPath = request.getServletPath();
if (session.getAttribute("user" ) == null && !requestPath.endsWith("/login.jsp" ) && !requestPath.endsWith("/LoginServlet" ) && !requestPath.endsWith("/RegisterServlet" )) {
request.getRequestD
大专栏 过滤器Filter ispatcher(
"login.jsp" ).forward(request, response);
} else {
chain.doFilter(request, response);
}
}
public void init (FilterConfig fConfig) throws ServletException {
}
}
装饰模式 职责:动态的为一个对象增加一个新功能,装饰模式是一种用于替代继承的技术,无需通过继承增加子类就能扩展对象的新功能。
当系统需要新的功能时,需要向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,但是这样增加了主类的复杂度,而且这些新加的代码可能仅仅是为了满足一些只在某些特定的情况下才会执行的特殊行为的需要,这时候就需要装饰模式,装饰模式把每个装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因当需要执行特殊行为时,客户代码就可以在运行时就可以根据需要有选择地、按顺序地使用装饰功能包装对象了;有效地把类的核心代码和装饰代码分离,去除相关类中的装饰逻辑。
装饰模式的实现
首先看需要被增强对象继承了什么接口或父类,编写一个类也去继承这些接口或父类。
在类中定义一个变量,变量类型即需增强对象的类型。
在类中定义一个构造函数,接收需增强的对象。
覆盖需增强的方法,编写增强的代码。
使用装饰模式增强requests对象 Servlet API中提供了一个requests对象的装饰模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper增强了requests对象的功能。
实现html标签转义功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.feng.controller;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
("/*" )
public class HtmlFilter implements Filter {
public void destroy () {
}
public void doFilter (ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
chain.doFilter(new HtmlRequest(request), response);
}
public void init (FilterConfig fConfig) throws ServletException {
}
}
class HtmlRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
public HtmlRequest (HttpServletRequest request) {
super (request);
this .request = request;
}
@Override
public String getParameter (String name) {
String value = this .request.getParameter(name);
if (value == null ) return null ;
return filter(value);
}
public String filter (String message) {
if (message == null )
return null ;
char content[] = new char [message.length()];
message.getChars(0 , message.length(), content, 0 );
StringBuilder result = new StringBuilder(content.length + 50 );
for (int i = 0 ; i < content.length; i++) {
switch (content[i]) {
case '<' :
result.append("<" );
break ;
case '>' :
result.append(">" );
break ;
case '&' :
result.append("&" );
break ;
case '"' :
result.append(""" );
break ;
default :
result.append(content[i]);
}
}
return (result.toString());
}
}
参考 http://www.cnblogs.com/xdp-gacl/p/3952405.html