参考:JavaWeb过滤器(Filter)详解
1、简介
顾名思义就是对事物进行过滤的,在Web中的过滤器,当然就是对请求进行过滤,我们使用过滤器,就可以对请求进行拦截,然后做相应的处理,实现许多特殊功能。如登录控制
,权限管理
,过滤敏感词汇
等。
只要请求或者响应符合配置的路径,不管路径对应的资源是否存在,都会进行过滤。
1.1、原理:
过滤器会对游览器的请求(和服务器返回的响应)
进行过滤,过滤器可以动态的分为3个部分:
- 1.放行之前的代码:对游览器请求进行第一次过滤,然后继续执行
- 2.放行(
filterChain.doFilter(servletRequest,servletResponse)
):将游览器请求放行,如果还有过滤器,那么就继续交给下一个过滤器 - 3.放行后的代码:对返回的Web资源再次进行过滤处理
这3个部分分别会发挥不同作用。
1.2、包:
servlet-api.jar
2、生命周期
Filter有3个阶段,分别是初始化,过滤,销毁。
- 初始化阶段:当服务器启动时,我们的服务器(Tomcat)就会读取配置文件,扫描注解,然后来创建我们的Filter。
- 过滤阶段:只要请求资源的路径和拦截的路径相同,那么过滤器就会对请求进行过滤,这个阶段在服务器运行过程中会一直循环。
- 销毁阶段:当服务器(Tomcat)关闭时,服务器创建的Filter也会随之销毁
它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。
过滤器生命周期:初始化init() → 过滤doFilter() → 析构(销毁)destroy()
3、使用
我们创建Filter,只需要继承Filter接口就行。
import javax.servlet.*;
import java.io.IOException;
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
}
3.1、接口、参数
3.1.1、接口
package javax.servlet;
public interface Filter {
default void init(javax.servlet.FilterConfig filterConfig) throws javax.servlet.ServletException { /* compiled code */ }
void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, javax.servlet.FilterChain filterChain) throws java.io.IOException, javax.servlet.ServletException;
default void destroy() { /* compiled code */ }
}
3.1.2、参数
FilterConfig和FilterConfig这2个对象是由服务器(Tomcat)在创建和调用Filter对象时所传入的,这2个对象十分有用。
- FilterConfig对象可以读取我们配置的初始参数;
- FilterChain可以实现多个Filter之间的连接。
public interface FilterConfig {
// 获取filter的名称
java.lang.String getFilterName();
// 获取ServletContext
javax.servlet.ServletContext getServletContext();
// 获取配置的初始参数的值
java.lang.String getInitParameter(java.lang.String s);
// 获取配置的所有参数名称
java.util.Enumeration<java.lang.String> getInitParameterNames();
}
/*
FilterChain就只有一个方法,其实这个方法就是用来对拦截进行放行的;
如果有多个拦截器,那么就会继续调用下一个Filter进行拦截。
doFilter方法需要传入个参数,一个是ServletRequest,一个是ServletResponse参数,这个直接传入进行。
Tomcat在调用过滤器时,默认就会传入Request和Response,
这个参数封装了请求和响应,我们直接使用就行。
ServletResquest和ServletResponse可以直接
强转成HttpServletRequest和HttpServletResponse,然后使用相应的方法
*/
public interface FilterChain {
void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws java.io.IOException, javax.servlet.ServletException;
}
①、FilterConfig
运用:在init方法中使用FilterConfig来读取配置的数据库的信息,然后输出。
<web-app>
<filter>
<filter-name>myFilterConfig</filter-name>
<!-- 过滤器对应的java类 -->
<filter-class>com.clucky.filter.MyFilterConfig</filter-class>
<!-- 初始化参数 -->
<init-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/equip_employ_manage?serverTimezone=GMT</param-value>
</init-param>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>root</param-value>
</init-param>
</filter>
<filter-mapping><!-- 映射 -->
<filter-name>myFilterConfig</filter-name>
<!-- 拦截路径:拦截所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-ap>
import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
public class MyFilterConfig implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("-----------获取全部key:value------------");
//得到所有配置参数的名字
Enumeration<String> names = filterConfig.getInitParameterNames();
while (names.hasMoreElements()) {
//得到每一个名字
String name = names.nextElement();
System.out.println(name+" = "+filterConfig.getInitParameter(name));
}
System.out.println("-----------end.....------------");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
@Override
public void destroy() {
}
}
②、FilterConfig
配置2个Filter,通过FilterChain来进行多个过滤。
第一个过滤器
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//注解方式配置Filter
@WebFilter("/*")
public class Filter01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("调用过滤器01对请求进行过滤~~~~");
//放行,如果还有过滤器,那么就执行下一个过滤器
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("调用过滤器01对响应进行过滤~~~~");
}
@Override
public void destroy() {
}
}
第二个过滤器
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class Filter02 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("调用过滤器02对请求进行过滤~~~~");
//放行,如果还有过滤器,那么就执行下一个过滤器
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("调用过滤器02对响应进行过滤~~~~");
}
@Override
public void destroy() {
}
}
Filter01先进行过滤,然后交给Filter02,然后访问资源,然后Filter02对响应进行过滤,然后Filter01对响应进行过滤。图示如下:
3.2、配置
- Filter 的 url-pattern 中配置的路径是访问已经存在的网络资源,如静态页面、jsp、servlet等。
- filter 和 url-pattern 之间的关系是 多对多的关系。即,Filter可负责拦截多个请求或响应;一个请求或响应也可被多个Filter拦截。(多个 filter 会根据 web.xml 中配置的顺序组成 filter 链)。
3.2.1、注解方式
@WebFilter(value = {"/*"},filterName = "myFilter")
public class test implements Filter {
}
@WebFilter源码
urlPatterns和value是一样的。urlPatterns和value只能配置一个,不能两个都配置,两个都配置就会报错。
public @interface WebFilter {
java.lang.String description() default "";
// filter显示名称
java.lang.String displayName() default "";
// 初始化参数
javax.servlet.annotation.WebInitParam[] initParams() default {};
// 该filter的名字
java.lang.String filterName() default "";
java.lang.String smallIcon() default "";
java.lang.String largeIcon() default "";
// 指定对哪些servlet进行过滤
java.lang.String[] servletNames() default {};
// 指定拦截路径
java.lang.String[] value() default {};
// 指定拦截路径
java.lang.String[] urlPatterns() default {};
javax.servlet.DispatcherType[] dispatcherTypes() default {javax.servlet.DispatcherType.REQUEST};
// 是否支持异步模式
boolean asyncSupported() default false;
}
3.2.2、xml方式
<web-app>
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.clucky.filter.MyFilter</filter-class>
</filter>
<!-- 一个过滤器需要过滤多种文件,则可以配置多个<filter-mapping>,
一个mapping定义一个url-pattern来定义过滤规则 -->
<filter-mapping>
<filter-name>myFilter</filter-name>
<!-- 所有外部访问都需要先经过该过滤器。 -->
<url—pattern>/*</url-pattern>
<!-- 作用于某一文件夹下所有文件 -->
<!-- <url—pattern>/dir/*</url-pattern> -->
<!-- 作用于某一种类型的文件 -->
<!-- <url—pattern>*.扩展名</url-pattern> -->
<!-- 作用于某一文件夹下某一类型文件 -->
<!-- <url—pattern>/dir/*.扩展名</url-pattern> -->
</filter-mapping>
</web-app>
3.3、执行顺序
多个Filter的执行顺序
如果配置了2个过滤器,怎么知道那个过滤器先执行呢?
- 如果我们是在web.xml中配置的过滤器,那么过滤器的执行顺序就是
<filter-mapping>
在web配置的顺序,配置在上面那么就会先执行。 - 如果我们是使用@WebFilter进行配置的,那么执行顺序就是字符比较顺序来执行,例如有2个过滤器,一个是AFilter,一个是BFilter,那么AFilter就会先执行。
- 如果注解和xml混用,那么在web.xml中配置的会先执行。
3.4、具体应用(过滤敏感词)
如果评论中含有我们定义的敏感词汇,那么我们就进行过滤,使用**来进行代替。
jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>评论</title>
</head>
<body>
<h1>输入评论内容</h1>
<form action="${pageContext.request.contextPath}/comment" method="post">
<textarea name="message" cols="30" rows="10"></textarea>
<input type="submit" value="提交">
</form>
<p >${requestScope.get("name")}<span style="color: red">${requestScope.get("comment")}</span></p>
</body>
</html>
requestScope介绍
Filter java
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// 这里使用注解配置,也可以用xml配置
@WebFilter(servletNames = {"comment"},initParams = {@WebInitParam(name = "sensitiveWord", value = "zz")})
public class CommentFilter implements Filter {
private List<String> sensitiveWords = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//得到敏感词汇
String word = filterConfig.getInitParameter("sensitiveWord");
//加入集合
sensitiveWords.add(word);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//设置编码
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
//得到评论
String message = servletRequest.getParameter("message");
for (String sensitiveWord : sensitiveWords) {
//对所有敏感词汇进行过滤
if (message.contains(sensitiveWord)){
//替换敏感词汇
message = message.replace(sensitiveWord, "**");
}
}
//存入request域
servletRequest.setAttribute("comment",message);
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
Servlet java
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.HashSet;
@WebServlet(name = "comment",value = "/comment")
public class CommentServlet extends HttpServlet {
//记录评论敏感词汇的ip
private HashSet<String> hashSet = new HashSet<>();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String message = request.getParameter("message");
String comment = (String) request.getAttribute("comment");
if (message.equals(comment)){
System.out.println("没有敏感词汇.....");
//设置名字
request.setAttribute("name","good boy:");
}else {
//有敏感词汇,记录IP
String localAddr = request.getLocalAddr();
System.out.println(localAddr);
hashSet.add(localAddr);
//设置名字
request.setAttribute("name","bad boy:");
}
//转发到comment.jsp页面
request.getRequestDispatcher("/comment.jsp").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
输入:你真是个zz
3.5、具体应用(解决乱码)
web.xml配置
<web-app>
<filter>
<filter-name>encoding</filter-name>
<filter-class>com.yyl.utils.EncodingFilter</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>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
java
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 强转
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
request.setCharacterEncoding("UTF-8") ;
// 网页编码编译的格式
response.setContentType ("text/html; charset=UTF-8") ;
filterChain.doFilter(request, response) ;
}
@Override
public void destroy() {
}
}