Filter过滤器
1.过滤器是什么?
过滤器: 可以把"不和谐"的东西给过滤掉/筛选掉/排除掉.
生活中的过滤器:带有过滤功能的净水器,滤纸,香烟的过滤嘴,测试,丈母娘.
程序中的过滤器:双向过滤器在Java中最小的程序单元是类,程序中的过滤器就是一个特殊的类.
Servlet/Filter是Web的一个组件.
2.过滤器的作用
我们可以简单理解为:过滤器处于客户端和服务端之间
过滤器可以对所有的请求或响应做拦截操作
-
以常规的方式调用资源(Servlet/JSP);
-
利用修改过的请求信息调用资源;
(可以将请求过来的数据修改后 再取调用资源)
-
调用资源之后,但在响应到客户端之前,对响应做出修改;
(可以将返回的响应修改后,交给客户端)
-
阻止当前资源调用,代之转到其他资源.
3.过滤器的应用
首先说明一下开发中常用的思想
1. DRY原则
Don't Repeat Yourself 开发中拒绝代码重复(重复会带来巨大的维护成本)
2. 责任分离原则
各自做各自最擅长的事情
过滤器在开发中的运用:
-
可以对请求中的字符做编码.
有时候我们会给Servlet设置编码格式
例如:request.setCharacterEncoding("UTF-8"); 但是要是很多Servlet都设置 造成了代码重复 所以我们可以在Filter中设置编码格式
-
登陆验证过滤器.
登陆验证操作 同样也是为了避免代码重复
将登陆验证操作写在Filter中 后面请求Servlet资源就不需要验证了
-
敏感字(非法文字)过滤.
有时候请求来的数据中会带有一些敏感词汇
经过Filter时 ,会将这些敏感词汇代替或者转义
-
做MVC框架中的前端控制器.(处理所有请求共同的操作,再分发)
4.过滤器的开发和使用
因为Servlet和Filter都是Web的组件,这里可以使用 我们可以进行对比记忆。
Servlet开发
1:自定义一个类(XxxServlet),实现于javax.servlet.Servlet接口.
2:实现Servlet接口中的方法(init(初始化方法),service(处理请求)).
3:告诉Tomcat来帮我们管理该Servlet程序(1:使用web.xml 做配置,2:WebServlet("/资源名")).
<servlet>
<servlet-name>Servlet的别名</servlet-name>
<servlet-class>自定义Servlet的全限定名</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet的别名</servlet-name>
<url-pattern>/资源名称</utl-pattern>
</servlet-mapping>
注意:此时的url-pattern的文本内容是外界访问是Servlet的资源名称.
Filter开发
1:自定义一个类(XxxFilter),实现于javax.servlet.Filter接口.
2:实现Filter接口中的方法(init(初始化方法),doFilter(执行过滤操作)).
在启动Tomcat的时候,就创建好对象,并调用init方法做初始化操作.
3:告诉Tomcat来帮我们管理该Filter程序(1:使用web.xml做配置,2:WebFilter("/资源名")).
<filter>
<filter-name>Filter的别名</filter-name>
<filter-class>自定义Filter的全限定名</filter-class>
</filter>
<filtert-mapping>
<filter-name>Filter的别名</filter-name>
<url-pattern>/资源名称</utl-pattern>
</filter-mapping>
注意:此时的url-pattern的文本内容是Filter对哪一些资源做过滤操作.
如: /hello.jsp :说明当前Filter只会对/hello.jsp做拦截/过滤.
/employee :说明当前Filter只会对/employee资源(Servlet)做过滤.
/system/* :说明当前Filter只会对以/system/作为前缀的资源路径做拦截.
FilterChain(过滤器链)
FilterChain(过滤器链):多个过滤器按照一定的顺序,排列起来.抽象的可以看为 多个过滤器组成一条链
程序中,存在多个过滤器的时候,过滤器的先后执行顺序由谁来决定?
由在web.xml中:配置的
过滤器的映射细节
1:Filter中的url-pattern的文本内容是Filter对哪一些资源做过滤操作.
如: /hello.jsp :说明当前Filter只会对/hello.jsp做拦截/过滤.
/employee :说明当前Filter只会对/employee资源做过滤.
/system/* :说明当前Filter只会对以/system/作为前缀的资源路径做拦截.
/* :说明对所有的资源做过滤.
注:url-pattern 可以有多个 可对很多资源同时做拦截操作
2:Filter的dispatcher(表示对哪些动作做过滤).
拦截器默认为只对请求做拦截
5.请求编码过滤器
CharacterEncodingFilter:
请求编码过滤器就是在过滤器中设置请求的数据的编码格式
这样就不需要在后面的Servlet中设置这一行代码了
req.setCharacterEncoding("UTF-8");
但是我们一般在源码中不能明文写自己设置的编码 存在硬编码问题
应该将编码格式设置在初始化参数中,从初始化参数获取
可以在web.xml中配置filter时 设置初始化参数 这样利于维护 只需要该配置文件即可
<!-- 配置初始化参数 自定义编码 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
将encoding设置为成员变量 之后可以在请求编码过滤器Filter的init方法中取出这个encoding
private String encoding;
@Override
public void init(FilterConfig config) throws ServletException {
// 编码要从配置web.xml中获取 不能用明文显示
this.encoding = config.getInitParameter("encoding");
}
得到设置的编码格式之后,我们在doFilter中开始设置
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 类型转换 转为http类型
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 应用中没有设置编码(req.getCharacterEncoding() == null 的情况)
// 并且我自己设置了编码 则设置。 若为false,就使用默认编码(ISO8859-1)
if (hasLength(encoding) && req.getCharacterEncoding() == null) {
req.setCharacterEncoding(encoding);
}
//放行
chain.doFilter(req, resp);
}
上述问题是第一种情况:应用中没有设置编码并且我自己设置了编码 则设置
但是我们还要考虑一种情况,就是之前已经有人设置了编码,我又定义了编码,此时是否使用我定义的编码
这里要引出一个 是否强制编码的问题 (是否使用我的编码)
是否强制编码问题
这里我们还是可以在web.xml中filter中设置一个参数 表示是否强制编码 默认为false
<!-- 设置是否强制编码 -->
<init-param>
<param-name>force</param-name>
<param-value>false</param-value>
</init-param>
跟上述方法一样 将其设置为成员变量 从init方法中获取
// 是否强制编码
private Boolean forceEncoding = false;
@Override
public void init(FilterConfig config) throws ServletException {
// 编码要从配置web.xml中获取 不能用明文显示
this.encoding = config.getInitParameter("encoding");
forceEncoding = Boolean.valueOf(config.getInitParameter("force"));
}
考虑这种情况时,设置编码的判断就要再加一项 判断是否要硬编码 这样就解决了编码问题
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 类型转换 转为http类型
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 设置编码
// 1.应用中没有设置编码(req.getCharacterEncoding() == null 的情况)并且我自己设置了编码
// 2.应用中已经存在编码但是要使用自定义的编码 : 强制使用
if (hasLength(encoding) && req.getCharacterEncoding() == null || forceEncoding) {
req.setCharacterEncoding(encoding);
}
//放行
chain.doFilter(req, resp);
}
6.登录验证过滤器
CheckLoginFilter
首先我们要明确 登录验证过滤器 是要过滤那些需要验证身份的页面
而登录页面和接收登录请求的Servlet是不需要被过滤的
所以,如果我们将CheckLoginFilter的url-pattern设置为 /* 则要将这两项去除过滤
<filter>
<filter-name>CheckLoginFilter</filter-name>
<filter-class>com.yhnit._03_checklogin.CheckLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CheckLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
我们可以将不需要过滤的资源定义在一个数组中,但是这里还是不严谨,不应该明文显示,应该从初始化参数中取出 但是这个过滤器用的不多,更多的是拦截器,所以我这里从简了
// 定义不需要被过滤的资源
// 其实这里不应该使用明文设置 应该从配置文件web.xml中获取初始化参数 后面要学拦截器
private String[] UncheckUris = {"/login.jsp","/login"};
doFilter方法 判断当前过滤资源是否是 不需要过滤的资源,如果是,则放行。 不是,则要身份判断。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 表示当前过滤的资源
String uri = req.getRequestURI();
System.out.println("当前被过滤的资源" + uri);
// 登陆页面和登陆Servlet不需要过滤
if (!Arrays.asList(UncheckUris).contains(uri)) {
Object user = req.getSession().getAttribute("USER_IN_SESSION");
if (user == null) {
resp.sendRedirect("/login.jsp");
return;
}
}
// 已经登录 放行
chain.doFilter(req, resp);
}
7.敏感字过滤器
敏感字过滤器 主要是客户端(浏览器)填写表单数据等信息时,存在敏感字,提交数据时,过滤器可以先将敏感字进行处理,比如转义或者变为*符号,然后Servlet获取的参数就是过滤器处理后的数据值
设计思想:
这里最主要的是 重写getParameter 这个方法 让Servlet处理请求时,用这个重写的方法,
因为重写的方法中有过滤功能
定义一个包装类MessageRequestWapper 里面重写getParameter 这个方法 方法里有过滤功能
在doFilter方法中将 请求req 包装为请求Servlet的请求
HttpServletRequest requestWapper = new MessageRequestWapper(req);
当请求Servlet时,Servlet中的req参数其实就是现在的包装后的请求requestWapper
然后调用包装类重写的getParameter 方法 即可完成过滤功能。
过滤功能实现(将敏感字写在stopwords集合中):