过滤器是Servlet的高级特性之一。但是也不必想的那么高深,归根结底也只是一个用来实现的Java类
我们来看看web容器中对数据的处理是怎么处理的:
从上面的图我们可以发现,当浏览器发送请求给服务器的时候,先执行过滤器,然后才访问Web的资源。服务器响应Response,从Web资源抵达浏览器之前,也会途径过滤器。。
我们很容易发现,过滤器可以比喻成一张滤网。我们想想现实中的滤网可以做什么:在泡茶的时候,过滤掉茶叶。那滤网是怎么过滤茶叶的呢?规定大小的网孔,只要网孔比茶叶小,就可以实现过滤了!
引申在Web容器中,过滤器可以做:过滤一些敏感的字符串【规定不能出现敏感字符串】、避免中文乱码【规定Web资源都使用UTF-8编码】、权限验证【规定只有带Session或Cookie的浏览器,才能访问web资源】等等等,过滤器的作用非常大,只要发挥想象就可以有意想不到的效果
也就是说:当需要限制用户访问某些资源时、在处理请求时提前处理某些资源、服务器响应的内容对其进行处理再返回、我们就是用过滤器来完成的!
举个简单的例子:用过滤器对数据进行编码的测试
为了方便,我们还是创建一个Maven项目,名字呢自己随便取,心里有数就好。然后写好简单得表单页面和Servlet处理页面;我这儿就直接贴截图了
其中index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户表单</title>
</head>
<body>
<h3>用户登录</h3>
<form action="filter" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
<input type="reset" value="重置">
</td>
</tr>
</table>
</form>
</body>
</html>
FilterServlet.java
import java.io.IOException;
public class FilterServlet extends javax.servlet.http.HttpServlet {
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
String username=request.getParameter("username");
System.out.println(username);
}
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
}
}
代码很简单,就十几行左右。我们打开浏览器看看效果
然后我们再去控制台看看
嗯哼。。。乱码了。
也就是说,如果一个网页中有多个表单验证,我们就要写很多行编码的代码。这样的话代码重复率太高了。而且代码的耦合度也低。那么这时候使用过滤器就会去过滤一遍我们的数据,然后对我们的数据进行编码。
我们这时候再看看,使用过滤器之后的数据格式
假如我们在过滤器中直接指定数据的编码格式,那么无论任何数据在到达服务器端的时候都会先走一遍过滤器,然后才能到达服务器。
只要Java类实现了Filter接口,那么这个类就可以当作一个过滤器。
其中的init()
方法和destroy()
方法不需要我们去过多的关注了。这就是他的启动与销毁方法。我们需要关注的是doFilter()
方法。
值得注意的是doFilter()方法,它有三个参数(ServletRequest,ServletResponse,FilterChain)
,从前两个参数我们可以发现:过滤器可以完成任何协议的过滤操作!
咦。。。怎么又是两个request和response参数。。这啥意思啊。
我们就可以这样去理解。里面的那个FilterChain相当于老二过滤器,外层的过滤器相当于老大过滤器。
举个例子:大家应该见过网上的那个净水器吧。一共有好多好多层。我们就可以理解为很多很多层过滤器。
好了,我们基础知识理解了,我们还是看看怎么去写一个过滤器吧。
上面我们说过,只要实现了Filter接口的都算是过滤器。那么我们就可以这样写;
import javax.servlet.*;
import java.io.IOException;
public class FilterServlet extends javax.servlet.http.HttpServlet implements Filter {
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
String username=request.getParameter("username");
System.out.println(username);
}
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
}
当然方法体写好了之后,同样Filter也是需要部署在服务器上的
打开我们的web.xml。然后在下面添加如下配置:
<filter>
<filter-name>FilterServlet</filter-name>
<filter-class>net.zxy.servlet.FilterServlet</filter-class>
<init-param>
<param-name>word_file</param-name>
<param-value>/WEB-INF/word.txt</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterServlet</filter-name>
<url-pattern>/*
:用于为过滤器指定一个名字,并且该字段不能为空
filter-class
:用于指定过滤器的实现类
init-parm
:用于为过滤器指定初始化参数,他的子元素是这个初始化参数的值
filter-name
:指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
filter-mappping
:用于设置一个Filter拦截的资源
filter-name
:必须和上面的filter-name对应相同
url-pattern
:设置 filter 所拦截的请求路径(过滤器关联的URL样式)。/*代表全部拦截
:指定过滤器所拦截的Servlet名称。
:指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
: 子元素可以设置的值及其意义:
上面已经说过了,过滤器的doFilter()方法是极其重要的,FilterChain接口是代表着所有的Filter,FilterChain中的doFilter()方法决定着是否放行下一个过滤器执行(如果没有过滤器了,就执行目标资源)。
测试1
试试在doFilter()方法中输出一句话然后再调用过滤器:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("我是过滤器1");
filterChain.doFilter(servletRequest, servletResponse);
}
访问页面,表单提交之后发现确实被执行了。而且还执行了三次(想一想为什么会被执行3次呢)。
测试2
我们再把doFilter()方法注释掉之后看看;
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("我是过滤器1");
// filterChain.doFilter(servletRequest, servletResponse);
}
我们看到,并没有任何输出,是因为我们打开服务器之后,主页面都没有任何内容;
测试3
我们在过滤器执行之后再看看,再试着打印出一段话看看会不会被执行
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("我是过滤器1");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("过滤器1执行完成,开始放行");
}
也就是说,我们在使用了过滤器之后,会自动的去过滤掉我们规定的信息。
filter有三种典型的应用
我们来做个小东西
禁止让浏览器缓存动态页面
我们先看看当前的响应头是怎么样的:
然后我们去设置他的响应头,再执行过滤器看看效果是什么样的
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
/*
* 让web资源不缓存,直接设置http中的response就行
* 由于我们使用的是http协议,ServletResponse中没有设置请求头的方法,所以我们要强制转换一下
* 直接转换成http类型的,,让他知道知道社会的黑暗
* */
HttpServletRequest request= (HttpServletRequest) servletRequest;
HttpServletResponse response= (HttpServletResponse) servletResponse;
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","np-cache");
response.setHeader("Pragma","no-cache");
// 上面的设置就是将目标资源设置成不缓存的了
filterChain.doFilter(servletRequest, servletResponse);
}
最后我们来使用过滤器来做一个简单得小Demo,实现自动登录;
public class User {
private String username,password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
实验数据库类UserDB
import net.zxy.bean.User;
import java.util.ArrayList;
import java.util.List;
public class UserDB {
private static List<User> users=new ArrayList<User>();
static {
users.add(new User("aaa","123"));
users.add(new User("bbb","213"));
users.add(new User("ccc","321"));
}
public static List<User> getUsers(){
return users;
}
public static void setUsers(List<User> users) {
UserDB.users = users;
}
}
Dao层UserDao
import net.zxy.bean.User;
import net.zxy.bean.beanDB.UserDB;
import java.util.List;
public class UserDao {
public static User find(String username,String password){
List<User> userList= UserDB.getUsers();
// 遍历集合
for (User user:userList){
if (user.getUsername().equals(username) && user.getPassword().equals(password)){
return user;
}
}
return null;
}
}
LoginServlet
package net.zxy.servlet;
import net.zxy.bean.User;
import net.zxy.dao.UserDao;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet(name = "LoginServlet",value = "/login")
public class LoginServlet extends HttpServlet implements Filter {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username=request.getParameter("username");
String password=request.getParameter("password");
UserDao userDao=new UserDao();
User user=userDao.find(username,password);
if (user==null){
request.setAttribute("errMsg","用户名或密码错误");
response.sendRedirect("login.jsp");
}
HttpSession session=request.getSession();
session.setAttribute("errMsg",user);
request.setAttribute("errMsg","你已登陆成功");
// 如果用户关闭了浏览器,那么这时候就需要Cookie了
Cookie cookie=new Cookie("autoLogin",user.getUsername()+"."+user.getPassword());
// 设置Cookie的生命周期
cookie.setMaxAge(Integer.parseInt(request.getParameter("time"))*60);
// 把Cookie返回给浏览器
response.addCookie(cookie);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response=(HttpServletResponse) servletResponse;
HttpServletRequest request=(HttpServletRequest) servletRequest;
// 如果用户没有关闭浏览器,那么就不需要Cookie
if (request.getSession().getAttribute("user")!=null){
filterChain.doFilter(request,response);
return;
}
// 如果用户关闭了浏览器,那么就需要Cookie中的信息来实现登录
Cookie[] cookies=request.getCookies();
String value=null;
for (int i=0;cookies!=null && i<cookies.length;i++){
if (cookies[i].getName().equals("autoLogin")){
value=cookies[i].getValue();
}
}
// 得到Cookie中的用户名和密码
if (value!=null){
String username=value.split("\\.")[0];
String password=value.split("\\.")[1];
UserDao userDao=new UserDao();
User user=userDao.find(username,password);
if (user!=null){
request.getSession().setAttribute("user",user);
}
}
filterChain.doFilter(request,response);
}
}
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户登录</title>
</head>
<body>
<h3>用户登录</h3>
<form action="login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="radio" name="time" value="10">10分钟
<input type="radio" name="time" value="30">30分钟
<input type="radio" name="time" value="60">60分钟
<br>
<input type="submit" value="登录">
</form>
</body>
</html>
展示结果自己去试试吧。。我没找到电脑的录屏工具。艹,不知道哪儿去了
我们在使用Cookie的时候会发现,我们的密码都是明文加上去。万一有个懂编程的瞅一眼,那不就是白搞了嘛,所以哦我们还需要对我们的密码进行加密处理一下。。具体代码就不写了,你们自己去搞定吧。嘿嘿嘿,就是这么任性。
在JavaWeb开发中,为了避免代码的重复性使用和表单数据的验证,我们通常会使用Filter(过滤器)对数据进行过滤一遍再发送到服务器,从而达到低耦合效果。
谢谢观看,本文参考自Java3y的博客,如有侵犯,请联系我删除。