Filter简介:
Filter也称之为过滤器,它是Servlet技术中最实用的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。
l特点:
•过滤器不是目标资源,是在访问目标资源的前后执行的。
•过滤器的拦截是双向的
•可以有多个过滤器。
•过滤器拦截是一堆目标资源。
Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,如下所示:
过滤器的工作原理
通过使用过滤器,可以拦截客户端的请求和响应,查看、提取或者以某种方式操作正在客户端和服务器之间进行交换的数据。
通过使用过滤器,可以对Web组件的前期处理和后期处理进行控制。
过滤器可以有多个,以构成一个过滤器链。Servlet容器会根据过滤器的配置情况来决定过滤器的调用次序。
过滤器Filter的实现和部署:
(1)实现一个过滤器Filter
定义的过滤器类必须要实现接口javax.servlet.Filter,并且实现该接口中定义的3个方法:
vod init(…):用于初始化过滤器。
void destroy():用于销毁过滤器。
void doFilter(…):用于执行过滤操作。
(2)部署一个过滤器Filter
在web.xml配置文件中部署Filter
用
TestFilter
filter.TestFilter
用
<url-pattern>用来设定被过滤的组件
TestFilter
/*.jsp
实例:
过滤器类:
package cn.itcast.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 实现Filter接口,重写方法
* 在web.xml进行配置
* @author Administrator
*/
public class FilterDemo1 implements Filter{
/**
* 初始化
*/
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* 每次请拦截的方式都执行(通过配置来决定)
* 由服务器调用doFilter() -- 进去到过滤器
* FilterChain服务器创建,把传入进来(过滤器的信息 )
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("站住,打劫!!");
// 放行
// 放行 -- 执行下一个过滤器 -- 没有访问目标资源
chain.doFilter(request, response);
// 去访问Servlet
// System.out.println("小伙,再来一次吧!!");
}
/**
* 销毁
*/
public void destroy() {
}
}
web.xml配置:
FilterDemo1
cn.itcast.filter.FilterDemo1
映射Filter示例:
testFilter
/test.jsp
testFilter
/index.jsp
REQUEST
FORWARD
映射Filter的多种方式:
•REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
•INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
•FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
•ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
Filter常见应用(1):
统一全站字符编码的过滤器
•通过配置参数encoding指明使用何种字符编码,以处理Html Form请求参数的中文问题
过滤器类:
web.xml配置文件:
package cn.itcast.demo1;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 解决全局的编码的问题
* @author Administrator
*
*/
public class EncodingFilter implements Filter{
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
/**
* 设置编码的问题
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String encoding = filterConfig.getInitParameter("encoding");
// 解决POST乱码的问题
request.setCharacterEncoding(encoding);
// 响应
response.setContentType("text/html;charset="+encoding);
// 放行
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Filter常见应用(2)
禁止浏览器缓存所有动态页面的过滤器:
•有 3 个 HTTP 响应头字段都可以禁止浏览器缓存当前页面,它们在 Servlet 中的示例代码如下:
•response.setDateHeader("Expires",-1);
•response.setHeader("Cache-Control","no-cache");
•response.setHeader("Pragma","no-cache");
•并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是同时使用上面的三个响应头。
•Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面
•Cache-Control响应头有两个常用值:
•no-cache指浏览器不要缓存当前页面。
•max-age:xxx指浏览器缓存页面xxx秒。
package cn.itcast.demo2;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class TimeFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 设置三个头信息
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", -1);
chain.doFilter(request, resp);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
TimeFilter
cn.itcast.demo2.TimeFilter
TimeFilter
*.jsp
Filter常见应用(3):
分ip统计访问次数
因为一个网站可能有多个页面,无论哪个页面被访问,都要统计访问次数,所以使用过滤器最为方便。
因为需要分IP统计,所以可以在过滤器中创建一个Map,使用IP为key,访问次数为value。当有用户访问时,获取请求的IP,如果IP在Map中存在,说明以前访问过,那么在访问次数上加1,即可;IP在Map中不存在,那么设置次数为1。
把这个Map存放到ServletContext中!
package cn.itcast.demo3;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class CountFilter implements Filter{
private FilterConfig config;
/**
* 进行初始化的操作
* 在ServletContext域中存入map
*/
public void init(FilterConfig config) throws ServletException {
// 先有一个MAP
Map countMap = new HashMap();
ServletContext context = config.getServletContext();
// 存
context.setAttribute("countMap", countMap);
this.config = config;
}
/**
* 该方法执行了
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
/**
* 1.获取map
* 2.获取ip
* 3.在map中和ip就对比
* * 如果map中有ip,获取count,+1
* * 如果map中没有ip,把ip和count=1存入到map中
* 4.把map存入到ServletContext中
* 5.放行
*/
ServletContext context = config.getServletContext();
// 获取map
Map countMap = (Map) context.getAttribute("countMap");
// 获取你的ip
String ip = request.getRemoteAddr();
// 判断map中是否存在该ip
Integer count = countMap.get(ip);
// 判断count为null
if(count == null){
// 第一次来
count = 1;
}else{
// 来过很多次了
count++;
}
// 把ip和count存入到Map中
countMap.put(ip, count);
// 向域中存入map
context.setAttribute("countMap", countMap);
// 放行
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
CountFilter
cn.itcast.demo3.CountFilter
CountFilter
/*
Filter常见应用(4):
实现用户自动登陆的过滤器
什么是自动登录?
自动登录就是必须先登录,并且选择自动登录的按钮,关闭浏览器,再次打开浏览器访问原来的登录页面的时候,会直接进入,不行再次登录。
•在用户登陆成功后,以cookis形式发送用户名、密码给客户端
•编写一个过滤器,filter方法中检查cookie中是否带有用户名、密码信息,如果存在则调用业务层登陆方法,登陆成功后则向session中存入user对象(即用户登陆标记),以实现程序完成自动登陆。
保证环境配置准备完毕,还需要在MySQL中创建表信息,与User封装类的字段相同。
后台程序:
MyjdbcUtil.java: 操作数据库的工具类,独立放在util工具包中:
package cn.itcast.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* 操作JDBC
* @author Administrator
*/
public class MyJdbcUtil {
public static ComboPooledDataSource dataSource = new ComboPooledDataSource();
/**
* 获取链接
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
/**
* 获取连接池
* @return
*/
public static DataSource getDataSource(){
return dataSource;
}
/**
* 释放资源
* @param rs
* @param stmt
* @param conn
*/
public static void release(ResultSet rs,Statement stmt,Connection conn){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
/**
* 释放资源的方法
* @param stmt
* @param conn
*/
public static void release(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
// 归还的方法
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
LoginServlet.java:
package cn.itcast.demo4;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 登陆的功能
* @author Administrator
*
*/
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = -8579027867816374707L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 获取参数
String username = request.getParameter("username");
String password = request.getParameter("password");
UserDao dao = new UserDao();
User existUser = dao.findUser(username, password);
if(existUser == null){
// 给提示
request.getRequestDispatcher("/demo4/login.jsp").forward(request, response);
}else{
// 把用户名和密码保存到cookie中,回写到浏览器。
String autologin = request.getParameter("autologin");
// 下一次自动登陆,把你的用户名和密码保存起来
if("auto_ok".equals(autologin)){
// 创建cookie,回写
Cookie cookie = new Cookie("autologin",username+"#itcast#"+password);
// 设置有效时间
cookie.setMaxAge(60*60);
// 回写
response.addCookie(cookie);
}
// 把用户的信息保存到session中
request.getSession().setAttribute("existUser", existUser);
response.sendRedirect(request.getContextPath()+"/demo4/suc.jsp");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
User.java:
package cn.itcast.demo4;
public class User {
private int id;
private String username;
private String password;
private String nickname;
private String type;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
UserDao.java:
package cn.itcast.demo4;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import cn.itcast.utils.MyJdbcUtil;
public class UserDao {
/**
* 通过用户名和密码查询单个用户
* @param username
* @param password
* @return
*/
public User findUser(String username,String password){
QueryRunner runner = new QueryRunner(MyJdbcUtil.getDataSource());
try {
return runner.query("select * from t_user where username = ? and password = ?", new BeanHandler(User.class), username,password);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
AutoLoginFilter.java: 过滤器实现自动登录功能
package cn.itcast.demo4;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 自动登陆的功能
* @author Administrator
*
*/
public class AutoLoginFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
/**
* * 在过滤器中可以直接获取session中的用户信息,如果user不为空,说明浏览器没关。放行。
* 从session中获取不到user的信息
* 先获取cookie,获取指定名称的cookie,
* 如果cookie为空,放行。
* 如果cookie不为空,获取用户名和密码。去数据库查询。
* 如果查询不到,cookie的信息不正确,放行(没有存入session中)。
* 如果查询到了,cookie中的信息是正确,把用户的信息保存到session中。放行。
*/
// 从session中获取用户的信息
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
// 从session获取用户的信息
User user = (User) session.getAttribute("existUser");
// 如果浏览器没关闭,session中的用户信息不为null的
if(user != null){
chain.doFilter(req, response);
}else{
// session中没有用户的信息
// 获取指定名称的cookie
Cookie [] cookies = req.getCookies();
// 获取到cookie,就可以进行判断了
Cookie cookie = getCookieByName(cookies,"autologin");
// 如果cookie为null
// 在你的浏览器中,根本就没有autologin的cookie
if(cookie == null){
// 直接放行 自己访问suc.jsp(因为suc.jsp已经做过处理了,没有session默认让你去登陆)了。
chain.doFilter(req, response);
}else{
// 从cookie中获取用户名和密码,去数据中查询
String username = cookie.getValue().split("#itcast#")[0];
String password = cookie.getValue().split("#itcast#")[1];
// 你需要去数据库中进行查询
UserDao dao = new UserDao();
// 去数据库中查询指定名称和密码的用户
User existUser = dao.findUser(username, password);
// 查询出的用户为null
if(existUser == null){
// 放行
chain.doFilter(req, response);
}else{
// 存入到session中
session.setAttribute("existUser", existUser);
// 放行
chain.doFilter(req, response);
}
}
}
}
public Cookie getCookieByName(Cookie [] cookies,String cookieName){
if(cookies == null){
return null;
}else{
for (Cookie cookie : cookies) {
// 判断
if(cookie.getName().equals(cookieName)){
return cookie;
}
}
return null;
}
}
public void destroy() {
}
}
AutoLoginFilter
cn.itcast.demo4.AutoLoginFilter
AutoLoginFilter
/*
前台程序:
login.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
Insert title here
登陆页面
suc.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Insert title here
亲,请登陆!
亲!欢迎访问:${ existUser.nickname },角色是:${ existUser.type }
添加商品
修改商品
删除商品
查看商品
Filter常见应用(5):
AuthorityFilter 权限过滤器
在一个系统中通常有多个权限的用户。不同权限用户的可以浏览不同的页面。使用Filter进行判断不仅省下了代码量,而且如果要更改的话只需要在Filter文件里动下就可以。
过滤器类:
package cn.itcast.demo5;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import cn.itcast.demo4.User;
/**
* URL级别全选验证
* @author Administrator
*
*/
public class CheckUserUrlFilter implements Filter{
private FilterConfig config;
/**
* 初始化的操作,获取初始化参数,读取到Map集合中。存入ServletContext中。
*/
public void init(FilterConfig config) throws ServletException {
// 创建Map保存信息
Map urlMap = new HashMap();
// 获取初始化参数,把参数的内容保存到urlMap中
Enumeration e = config.getInitParameterNames();
while(e.hasMoreElements()){
// 获取到的值
String paramName = e.nextElement();
// 获取到的是的值
String paramValue = config.getInitParameter(paramName);
// 存入Map集合中 {/admin:admin} {/user:user}
urlMap.put(paramName, paramValue);
}
// 存入到ServletContext中
config.getServletContext().setAttribute("urlMap", urlMap);
this.config = config;
}
/**
* 权限的验证
* * 从请求的链接中拿到链接和urlMap中的链接和type类型做对比
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 先获取请求的链接 请求:/admin/add.jsp map {/admin:admin} {/user:user}
HttpServletRequest req = (HttpServletRequest) request;
// /day21/admin/add.jsp
// String uri = req.getRequestURI();
// System.out.println(uri);
//
// 获取请求的链接
String sPath = req.getServletPath();
// System.out.println(sPath);
// 和Map来进行对比
ServletContext context = config.getServletContext();
// 获取Map的集合了
Map urlMap = (Map) context.getAttribute("urlMap");
// 做对比 set的key值 /admin /user
Set set = urlMap.keySet();
// 循环 /admin
for (String paramName : set) {
// /admin/add.jsp从/admin开始的
if(sPath.startsWith(paramName)){
// 去获取session的用户信息,从用户的信息中获取type属性的值,和Map中的value的值进行对比。
User user = (User) req.getSession().getAttribute("existUser");
// 如果user为null
if(user == null){
// 转发到登陆的页面上
req.getRequestDispatcher("/demo4/login.jsp").forward(req, response);
return;
}else{
// 去判断用户的type的值和我Map中的value的值做对比
String userType = user.getType();
// 还有一个Map中存了一个值 {/admin admin} {/user user}
String paramValue = urlMap.get(paramName);
if(userType.equals(paramValue)){
// 匹配成功了 /admin/add.jsp 做过一次判断了,已/admin开头的,
// 从用户的信息中获取type 的值和Map的值做了对比
chain.doFilter(req, response);
return;
}else{
// 向用户提示
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("亲,您权限不够!!
");
return;
}
}
}
}
chain.doFilter(req, response);
}
public void destroy() {
}
}
web.xml配置文件:
CheckUserUrlFilter
cn.itcast.demo5.CheckUserUrlFilter
/admin
admin
/user
user
CheckUserUrlFilter
/*
Filter高级开发:
由于开发人员在filter中可以得到代表用户请求和响应的request、response对象,因此在编程中可以使用Decorator(装饰器)模式对request、response对象进行包装,再把包装对象传给目标资源,从而实现一些特殊需求。
Decorator设计模式的实现
•1.首先看需要被增强对象继承了什么接口或父类,编写一个类也去继承这些接口或父类。
•2.在类中定义一个变量,变量类型即需增强对象的类型。
•3.在类中定义一个构造函数,接收需增强的对象。
•4.覆盖需增强的方法,编写增强的代码。
request对象的增强:
Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper , (HttpServletRequestWrapper 类实现了request 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法)以避免用户在对request对象进行增强时需要实现request接口中的所有方法。
使用Decorator模式包装request对象,完全解决get、post请求方式下的乱码问题