Filter过滤器··简介:
- Filter过滤器是一个小型的web组件(web三大组件之一), 可以动态地拦截url请求和响应, 以便查看、提取或操作包含在url请求和响应中的信息。
Filter过滤器的执行流程:
-
客户端发送请求后, Filter过滤器调用过滤器的doFilter方法对请求做预处理
-
预处理完成之后, 请求会传递到servlet.service()方法进行正式处理, 并产生处理结果
-
紧接着, Filter过滤器会拦截响应结果, 并调用doFilter方法对响应结果做后处理
-
然后, 将响应结果返回给客户端
javax.servlet.Filter接口
自定义过滤器, 需要实现javax.servlet.Filter接口, 下面是Filter接口的源码:
package javax.servlet;
import java.io.IOException;
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
-
1、init()方法, 的形参是一个FilterConfig接口对象, 可以调用FilterConfig对象中的方法来获取过滤器的初始化参数、过滤器名称等操作. . .
- init()方法会在web服务器启动创建过滤器对象时被调用, 并且只会执行一次。
package javax.servlet; import java.util.Enumeration; public interface FilterConfig { String getFilterName(); ServletContext getServletContext(); String getInitParameter(String var1); Enumeration<String> getInitParameterNames(); }
- 2、doFilter()方法, 该方法中有三个参数:servletRequest、servletResponse 和 FilterChain。
-
servletRequest、servletResponse用于对请求和响应进行处理。
-
filterChain是一个接口对象, filterChain对象中提供了一个doFilter()方法, 当我们调用filterChain对象的doFilter()方法后,就会将请求转发给过滤器链的下一个过滤器, 当最后一个过滤器调用filterChain对象的doFilter()方法后, 就会将请求转发给Servlet, web服务器就会调用Servlet的service()方法, 这样就可以访问到拦截过滤的web资源了, 否则拦截的web资源不会被访问。
-
每一次执行过滤器都会调用doFilter方法。
-
- 3、destroy()方法, 会在web服务器关闭 或 重启时调用, 销毁过滤器对象。
应用场景:
【 场景一 】
-
问题:表单提交时, 如果使用的是post方式提交数据, 提交的数据中如果含有中文汉字, 那么就会出现中文乱码问题。
-
解决1:Servlet接受到请求后, 先通过request.setCharacterEncoding(“utf-8”);来设置请求的编码字符集, 然后再通过request.getParameter(“xxx”);来获取请求中的参数, 这样获取到的url参数就不会出现中文乱码问题。但这种方式需要在每个Servlet中都加上request.setCharacterEncoding(“utf-8”); 导致代码重复,且不利于后期维护。
-
解决2:把设置请求和响应编码字符集的代码抽取出来, 放置到自定义过滤器中, 然后配置拦截所有请求, 把拦截的所有请求的编码字符集都设置为utf-8。
【 场景二 】
-
问题:我们在访问一个网站的某个界面时, 可以在浏览器地址栏中输入相应地址, 就可以直接访问到所需页面。这样导致我们可以将数据库修改的一塌糊涂。
-
解决:设置登陆权限验证, 在访问网站的一些隐私界面时, 需要先进行登陆。如果未登陆就直接访问隐私界面, 就会进行拦截, 然后将其重定向回登陆界面。但有一些请求是不需要拦截的, 比如去登陆的请求、登陆的请求等. . .
创建一个form表单, 以post请求提交数据, 验证中文乱码现象
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>home</title>
</head>
<body>
<div class="main">
<p>字符串拼接器</p>
<form action="/encodingServlet" method="post">
<p><input type="text" name="string1" placeholder="字符串1:"></p>
<p><input type="text" name="string2" placeholder="字符串2: "></p>
<input type="submit" value="提交">
</form>
</div>
</body>
</html>
创建一个servlet, 对请求进行逻辑处理。
package com.cd4356;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(urlPatterns = "/encodingServlet",loadOnStartup = 1)
public class EncodingServlet extends HttpServlet {
public EncodingServlet() {
System.out.println("正在创建FirstServlet对象");
}
@Override
public void init() throws ServletException {
System.out.println("正在初始化FirstServlet对象");
}
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("正在执行FirstServlet服务");
String str1=request.getParameter("string1");
String str2=request.getParameter("string2");
String str = str1+str2;
PrintWriter out = response.getWriter();
out.println("<h2>");
out.println("拼接结果为: "+str);
out.println("</h2>");
}
@Override
public void destroy() {
System.out.println("正在销毁FirstServlet对象");
}
}
使用post方式提交数据, 且提交的数据中含有中文, 所以出现了中文乱码问题。
解决方法一: Servlet接受到请求后, 先通过request.setCharacterEncoding(“utf-8”);来设置请求的编码字符集为utf-8, 再获取请求中的参数, 这样就可以解决中文乱码问题了。
【缺点】: 需要在每个Servlet中都加上request.setCharacterEncoding(“utf-8”); 导致代码重复,且不利于后期维护。
package com.cd4356;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(urlPatterns = "/encodingServlet",loadOnStartup = 1)
public class EncodingServlet extends HttpServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
//设置请求和响应的编码字符集
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
String str1=request.getParameter("string1");
String str2=request.getParameter("string2");
String str = str1+str2;
PrintWriter out = response.getWriter();
out.println("<h2>");
out.println("拼接结果为: "+str);
out.println("</h2>");
}
}
解决方法二:
1、自定义一个EncodingFilter编码过滤器来实现Filter接口(注意:该Filter接口位于javax.servlet包下), 并重写init()
、doFilter()
和 destory()
。将设置请求和响应编码字符集这部分抽取出来, 放到自定义编码过滤器的doFilter方法中。
package com.cd4356;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class EncodingFilter implements Filter {
private String encoding;
@Override
public void init(FilterConfig config) throws ServletException {
if(config.getInitParameter("encoding").equals("utf-8")){
encoding =config.getInitParameter("encoding");
}
encoding="utf-8";
}
//过滤器的关键方法,完成过滤器的功能(对请求进行预处理和对响应结果后处理)
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
chain.doFilter(request,response);
}
//用来定义过滤器销毁时做的一些操作
@Override
public void destroy() {
}
}
2、定义了编码过滤器后, 还是无法使用的, 还需要我们到web.xml中对过滤器注册声明, 并映射到指定的url中。然后web服务器就会根据web.xml文件中的配置信息来创建每个注册的Filter过滤器对象, 并保存在服务器内存中。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--编码过滤器,处理中文乱码 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.cd4356.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<!--使用/*通配符,拦截所有资源请求,包括图片、等静态资源的请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
要求:
1、设置请求和响应的编码字符集, 防止中文乱码。
2、用户输入账号密码 ----> 验证(判断用户是否存在) ----> 验证失败(账号密码错误),则重定向回登陆页面。
3、用户在地址栏中输入具体请求 ----> 拦截请求 ----> 判断用户是否登陆(session中是否存在用户信息) ----> 已登录,则放行 / 未登录,则拦截并重定向回登陆页面。
【登陆页面】login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>login</title>
<style>
.main{
width: 300px;
text-align: center;
padding: 20px;
margin-top: 30px;
margin-left: 30px;
box-shadow: 0 0 5px 5px #ccc;
}
</style>
</head>
<body>
<div class="main">
<p>登陆</p>
<form action="/loginServlet" method="post">
<p><input type="text" name="account" placeholder="账号:"></p>
<p><input type="text" name="password" placeholder="密码: "></p>
<input type="submit" value="提交">
</form>
</div>
</body>
</html>
【主页面】home.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>home</title>
</head>
<body>
<h3>欢迎你,来到xxx主页面!</h3>
</body>
</html>
【 User实体类, 保存登陆用户信息】
package com.cd4356;
public class User {
private String account;
private String password;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
【登陆验证】
package com.cd4356;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/loginServlet",loadOnStartup = 1)
public class LoginServlet extends HttpServlet {
@Override
public void init() throws ServletException {
super.init();
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取url中的account和password参数
String account = request.getParameter("account");
String password = request.getParameter("password");
if (account.equals("aaa") && password.equals("bbb")){
//将获取到的参数封装到user对象中
User user = new User();
user.setAccount(account);
user.setPassword(password);
//将user对象存到session中
request.getSession().setAttribute("user",user);
//重定向到home.html页面
response.sendRedirect(request.getContextPath()+"/home.jsp");
return;
}
response.sendRedirect(request.getContextPath()+"/login.jsp");
return;
}
@Override
public void destroy() {
super.destroy();
}
}
【编码过滤】
package com.cd4356;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class EncodingFilter implements Filter {
private String encoding;
@Override
public void init(FilterConfig config) throws ServletException {
if(config.getInitParameter("encoding").equals("utf-8")){
encoding =config.getInitParameter("encoding");
}
encoding="utf-8";
}
//过滤器的关键方法,完成过滤器的功能(对请求进行预处理和对响应结果后处理)
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
chain.doFilter(request,response);
}
//用来定义过滤器销毁时做的一些操作
@Override
public void destroy() {
}
}
【登陆过滤】
package com.cd4356;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginFilter 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;
String url = request.getRequestURI();
if (url.toLowerCase().indexOf("login")>=0){
filterChain.doFilter(request,response);
return;
}
//从session中获取登陆用户信息
User user = (User) request.getSession().getAttribute("user");
//用户未登陆,则重定向回登陆界面
if(user==null){
response.sendRedirect(request.getContextPath()+"/login.jsp");
return;
}
//已登录,则继续执行过滤器链的下一个组件(下一个过滤器)
filterChain.doFilter(request,response);
return;
}
@Override
public void destroy() {
}
}
【注册过滤器】web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--编码过滤器,处理中文乱码 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.cd4356.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.cd4356.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<!--使用/*通配符,拦截所有资源请求,包括图片、等静态资源的请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
1、在web应用程序中, 可以不配置过滤器、可以配置多个过滤器、也可以配置多个过滤器。如果配置了多个过滤器, 则这些过滤器就会相连以链的形式执行
2、过滤器链中, 过滤器的执行顺序与web.xml中<filter-mapping>的声明顺序有关(与<filter>无关), 遵从先声明、先执行的规则。
以场景2案例为例, EncodingFilter过滤器的<filter-mapping>先声明, 所以先执行EncodingFilter过滤器, 后执行LoginFilter过滤器。
如果想要反转过滤器的执行顺序, 只需在web.xml中反转<filter-mapping>的声明顺序即可。其实, 如果定义的Filter过滤器之间没有关联,那么, 我们就无需关心Filter过滤器的执行顺序。
Filter过滤器执行顺序总结:
-
1、基于web.xml配置, 则过滤器执行顺序与<filter-mapping>相关, 遵从先声明、先执行的规则。
-
2、基于注解配置, 则过滤器执行顺序与过滤器名称首字母顺序有关, 比如encodingFilter 和 loginFilter, e在l前面, 所以EncodingFilter过滤器先执行
@WebFilter(filterName = "loginFilter",urlPatterns = "/*") public class LoginFilter implements Filter { //write your code }
@WebFilter(filterName = "encodingFilter", initParams = { @WebInitParam(name = "encoding",value = "utf-8") }, urlPatterns = "/*") public class LoginFilter implements Filter { //write your code }
-
3、如果部分过滤器基于web.xml配置, 另一部分基于注解配置。则基于web.xml配置的过滤器先执行
Spring MVC框架给我们提供了一个CharacterEncodingFilter编码过滤器,我们在开发Spring MVC项目过程中,可以自定义编码过滤器,也可以使用Spring MVC提供的CharacterEncodingFilter编码过滤器
那么,自定义编码过滤器 和 CharacterEncodingFilter编码过滤器有什么区别呢?
- 其实它们的功能都是一样, 但使用CharacterEncodingFilter编码过滤器可以省略自定义编码过滤器这一步, 我们直接在web.xml文件中注册使用即可, 在一定程度上简化了我们的开发。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--注册CharacterEncodingFilter过滤器-->
<filter>
<filter-name>encodingFilter</filter-name>
<!--指定过滤器的类(全类名),web服务器才会对该过滤器类进行对象的创建-->
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--过滤器参数初始化-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<!--使用/*通配符,拦截所有请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>