1. 过滤器概念
- 作用:过滤用户的请求和响应,修改用户的请求和响应的数据,对请求进行拦截。
- 应用场景:
- 统一解决post提交请求乱码应用;
- 过滤用户发表内容中的非法字符;
- 登录权限检查;
2. 过滤器开发
2.1 过滤器开发步骤
-
创建一个类实现过滤器接口
-
注解配置过滤器拦截的请求路径(
urlPatterns = "映射路径"
) -
在doFilter方法中书写过滤任务
-
FilterChain.doFilter方法放行
注意事项
doFilter默认拦截请求,如通过过滤器后可继续访问资源则filterChain放行
2.1.1 简单示例代码
过滤器代码
//urlPatterns是写需要过滤的映射路径
@WebFilter(filterName = "Filter01",urlPatterns = "/Demo01Servlet")
public class Filter01 implements Filter {
public void destroy() {
System.out.println("Filter01被销毁了");
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
/*在doFilter方法前的内容是请求过滤的代码*/
System.out.println("Filter01执行请求过滤");
chain.doFilter(req, resp);
/*doFilter方法后的内容是响应过滤的代码*/
System.out.println("Filter01执行响应过滤");
}
public void init(FilterConfig config) throws ServletException {
System.out.println("Filter01被实例化了");
}
}
服务器代码
@WebServlet(urlPatterns = "/Demo01Servlet")
public class Demo01Servlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Demo1Servlet处理请求执行");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//*如果post方式的执行内容与get不同,则删掉下面代码重写即可
doGet(request, response);
}
}
执行的结果
Filter01被实例化了
Filter01执行请求过滤
Demo1Servlet处理请求执行
Filter01执行响应过滤
2.2 过滤器执行流程
2.3 过滤器的生命周期
2.3.1 init方法
- 由服务器在启动的时候执行一次,对过滤器进行实例化。
2.3.2 doFilter方法
- 对每个符合Filter映射路径的请求,doFilter都会执行。如果需要对这个请求通过过滤器继续访问资源,就必须调用FilterChain对象的doFilter方法对请求放行。
2.3.3 destroy方法
- 销毁过滤器。在服务器停止或移除服务器的项目时调用一次。
- 可以在这个方法中通知垃圾回收机制更早的回收内存,从而释放内存。执行代码为
objectList = null;
3. 映射路径
- 通过设置映射路径可以对不同的请求有选择性地过滤。
3.1 精确匹配模式
-
请求的路径需要与过滤器配置的路径完全一致,才能被这个过滤器过滤。
-
可以精确匹配到一个网页(html、jsp)、一个servlet、一个图片文本等资源。
-
注意:如果过滤器路径为/a.html,那么只有访问
http://localhost:8080/a.html
(假设访问的是本地服务器)才会被过滤,像http://localhost:8080/aa/a.html
之类包含/a.html的请求不会被过滤
3.2 模糊匹配模式
- 使用通配符“/*”
- 特点和使用要求:
*
必须位于结尾;*
前面必须有/
,否则*
只能视为普通字符
3.2.1 /*
整个应用的配置
- 浏览器发出访问当前项目的任何请求都会经过当前过滤器
3.2.2 /指定目录名/*
指定目录下的路径配置
- 浏览器发出访问当前项目的指定目录下的任何请求都会经过当前过滤器
3.3 映射路径示例代码
3.3.1 单一过滤器对多个映射路径过滤
-
方法:在过滤器注解中添加多个映射路径
-
格式:
@WebFilter(filterName="过滤器名", urlPatterns={"拦截路径1","拦截路径2","拦截路径3"....})
3.3.2 示例代码
需求:展现精确匹配模式和模糊匹配模式
@WebFilter(filterName = "Filter02",urlPatterns = {"/index.jsp","/img/1.jpg","/test/*"})
public class Filter02 implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("Filter02过滤请求");
chain.doFilter(req, resp);
System.out.println("Filter02过滤响应");
}
public void init(FilterConfig config) throws ServletException {
}
}
效果:
访问index.jsp、img/1.jpg和test目录下所有资源都经过过滤器
4. 拦截方式
- 根据不同的访问资源方式设置不同的拦截方式
4.1 request(默认拦截方式)
- 浏览器发出的请求都会进行拦截,包括直接从地址栏访问和重定向到映射路径两种情况。
4.2 forward
-
对服务器内部请求转发进行拦截。
-
使用方式,在注解代码中添加以下代码
dispatcherTypes = DispatcherType.FORWARD
-
注意:在添加上述代码后,request方式会无效!
@WebFilter(filterName = "Filter03",urlPatterns = "/DemoServlet03",dispatcherTypes ={DispatcherType.FORWARD)
public class Filter03 implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
System.out.println("Filter03的请求过滤代码");
chain.doFilter(request, response);
System.out.println("Filter03的响应过滤代码");
}
public void init(FilterConfig config) throws ServletException {
}
}
4.3 两种拦截方式共同过滤
-
使用方式:在注解代码中添加以下代码
dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST}
-
设置完后,则所有直接访问或转发映射路径的请求都会进入过滤器。
@WebFilter(filterName = "Filter04",urlPatterns = "/DemoServlet04",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
public class Filter04 implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
System.out.println("Filter04的请求过滤代码");
chain.doFilter(request, response);
System.out.println("Filter04的响应过滤代码");
}
public void init(FilterConfig config) throws ServletException {
}
}
5. 过滤器链
5.1 概念
- 多个过滤器组成的一个整体我们称为过滤器链。
- 过滤器链共同对同一个请求进行过滤。
- 过滤器链中的过滤器是一个接一个的执行的,直至全部过滤器方形后,才会访问到目标资源。
- 只要其中一个过滤器没有放行,那么这个过滤器后面的过滤器也都不会执行了。
5.2 过滤器链的执行流程
5.3 过滤器链的设置方式
5.3.1 过滤器注解方式
- 执行顺序:根据过滤器类名字字符串比较进行升序排序
- 注意:该方式的执行顺序跟类的名字有关,与filterName没有关系!!
@WebFilter(filterName = "Chain1Filter",urlPatterns = "/DemoServlet05")
public class Chain1Filter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("Chain1Filter请求过滤");
chain.doFilter(req, resp);//放行
System.out.println("Chain1Filter的响应过滤");
}
public void init(FilterConfig config) throws ServletException {
}
}
@WebFilter(filterName = "Chain2Filter",urlPatterns = "/DemoServlet05")
public class Chain2Filter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("Chain2Filter请求过滤");
chain.doFilter(req, resp);//放行
System.out.println("Chain2Filter的响应过滤");
}
public void init(FilterConfig config) throws ServletException {
}
}
执行顺序:
Chain1Filter请求过滤
Chain2Filter请求过滤
DemoServlet05执行
Chain2Filter的响应过滤
Chain1Filter的响应过滤
5.3.2 web.xml配置方式
- 过滤器链执行顺序由配置文件中过滤器的顺序决定,从上到下顺序执行。
- 注意:该方式的执行顺序跟过滤器类名字没有关系!!
<!--配置过滤器-->
<filter>
<filter-name>Chain2Filter</filter-name>
<filter-class>azure.filter.Chain2Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>Chain2Filter</filter-name>
<url-pattern>/DemoServlet05</url-pattern>
</filter-mapping>
<filter>
<filter-name>Chain1Filter</filter-name>
<filter-class>azure.filter.Chain1Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>Chain1Filter</filter-name>
<url-pattern>/DemoServlet05</url-pattern>
</filter-mapping>
6. 经典案例-非法字符
- 需求:表单中提交的数据包含非法字符时提示言论非法。可以视为一个工具类。
准备工作:
-
一个需要提交数据的表单的网页,其中输入栏的input标签中的name属性=“message”;
-
一个txt文件,里面存入非法字符。注意,每个非法字符均占一行;
-
一个服务器负责接收表单(本例中服务器servlet的urlPatterns=“Demo06Servlet”);
过滤器代码:
@WebFilter(filterName = "Filter03",urlPatterns = "DemoServlet")
public class Filter03 implements Filter {
//使用一个集合保存非法字符
List<String> list = new ArrayList<>();
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//父接口转化为子接口
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
//解决请求和相应乱码问题
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//获取请求的内容
String message = request.getParameter("message");
if (list != null && list.size() > 0){
//遍历集合对比内容
for (String str : list) {
if (message.contains(str)) {
response.getWriter().write("<script>alert('您输入的内容包含敏感内容');history.back();</script>");
//只要找到就无须继续寻找,结束整个方法
return;
}
}
}
//如果没有找到非法字符,那么就跳转到服务器即可
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
/*
在过滤器实例化时就读取非法字符的集合;
使用缓冲流读取数据
*/
//获得真实路径,注意,非法字符文件是放在WEB-INF目录下(如有子文件夹则写上)
try {
String realPath = config.getServletContext().getRealPath("/WEB-INF/source/illegalStr.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(realPath)));
//每读取一行存入集合
String len = null;
while ((len = br.readLine()) != null) {
list.add(len);
}
//记得关闭流释放资源
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7. 解决全站乱码
- 仅可以解决Tomcat8的乱码,可以作为工具类使用
/*
解决全站乱码过滤器,目前只适合tomcat8,其余版本无效
*/
@WebFilter(filterName = "EncodingFilter",urlPatterns = "/*")
public class EncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//将父接口转换为子接口
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//解决全站乱码
//1.处理post请求乱码
//获取请求类型
String method = request.getMethod();
if(method.equalsIgnoreCase("post")){
request.setCharacterEncoding("utf-8");
}
//2.处理response响应乱码
response.setContentType("text/html;charset=utf-8");
chain.doFilter(request, response);
}
public void init(FilterConfig config) throws ServletException {
}
}