过滤器
在多个页面需要统一执行的代码,可以通过过滤器的web组件来完成;
- 定义过滤器
//识别过滤器
@WebFilter(urlPattern="要过滤哪些路径")
class 过滤器类 implements Filter{
//初始化操作
public void init(){}
//销毁操作
public void destroy(){}
//过滤方法
//FilterChain过滤器链
public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain){
//要统一执行的代码
//是否让请求继续前进
chain.doFilter(req,resp);
}
}
- 过滤的路径格式
- 精准匹配 /servlet1:只会进入servlet1之前进入过滤器;
- 后缀匹配 *.jsp:访问任意一个以.jsp结尾的路径都会经过过滤器;
- 前缀匹配 /user/*:当请求的路径是以user开头的就会经过过滤器;
如果有多个过滤器都和目标路径匹配,那么会依次经过多个过滤器,经过先后顺序跟过滤器的名字有关;
- request和response的类型转换
Filter中的request和response声明的是父类型,有些方法没有,需要转换为子类型对象;
- 过滤器的应用
- post请求的中文乱码,可以采用字符编码过滤器来解决
req.setCharacterEncoding("解码字符集");
req.getParameter()
四种拦截方式
- 请求 dispatcher
- 转发 forward
- 包含 include
- 错误 error
过滤器的应用场景
- 执行目标资源之前的预处理工作,例如设置编码;
- 通过条件是都放行;
- 在目标资源执行后,做一些后续的特殊处理工作,例如把目标资源输出的数据进行处理。
案例
登录检查过滤器
@WebFilter(urlPatterns = "/*")
public class Filter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
//检查路径是否为登录界面路径,如果是则直接放行,否则判断用户是否已经登录
String uri = req.getRequestURI();
if (uri.equals("/users.jsp") || uri.equals("/servlet01")){
filterChain.doFilter(req,resp);
return;
}
//检查会话中是否存在username用户,如果存在这说明已经登录,否则跳转到登录页面让用户继续登录
HttpSession session = req.getSession();
Object user = session.getAttribute("username");
if(user ==null){
session.setAttribute("error","您尚未登录");
resp.sendRedirect("/users.jsp");
}else {
filterChain.doFilter(req,resp);
}
}
@Override
public void destroy() {
}
}
如果跳转页面有多层目录,在跳转时在路径前加上"/",以相对主机名和端口号进行跳转;
否则会以从后往前第一个"/"之前的路径为相对路径进行跳转。
自动登录过滤器
- 浏览器第一次登陆,发送登录请求,登陆成功后,服务器将包含用户名、密码的登录标志信息放入session中;
- 服务器返回给浏览器一个cookie响应,包含用户名和密码,并且浏览器设置maxAge等属性;
- 浏览器关闭,session中的信息失效,但是浏览器中存在cookie的信息,在发送请求的时候,
同时将cookie中的信息发送给服务器; - 过滤器先检查session内容,如果有旧直接放行,否则检查cookie中的信息,看cookie是否存在并且用户名和密码是否正确,
如果正确直接放行,否则需要重新登录;
在servlet中判断是否要自动登录
//如果自动登录栏被勾选则执行
if (req.getParameter("autoLogin").equals("true")){
req.setAttribute("success","登陆成功");
//创建cookie对象,将用户信息存入
Cookie cookie = new Cookie("up",user.getUsername()+":"+user.getPassword());
//设置cookie的存活时间
cookie.setMaxAge(24*3600);
//将cookie添加到响应,返回给浏览器
resp.addCookie(cookie);
}
在过滤器中,先判断session中是否存在用户信息,如果存在则自动登录;如果不存在则判断浏览器发过来的
cookie中是否存在用户信息,如果存在则自动登录,并且把cookie中的信息放入session中方便以后使用,否则跳转到登录页面;
//检查会话中是否存在username用户
HttpSession session = req.getSession();
Object user = session.getAttribute("username");
if(user !=null){
filterChain.doFilter(req,resp);
}else {
//如果session中没有用户信息,则检查cookie中是否存在
//获取浏览器请求时发过来的cookie
Cookie[] cookies = req.getCookies();
Cookie up=null;
for (Cookie cookie : cookies) {
if(cookie.getName().equals("up")){
up=cookie;
break;
}
}
if(up!=null){
//获取用户名和密码
String[] split = up.getValue().split(":");
//验证用户名和密码
UserDao ud = new UserDao();
String username = split[0];
String password=split[1];
User user1 = ud.authUser(username);
if (user1!=null && user1.getPassword().equals(password)){
//将用户名放入session
session.setAttribute("username",username);
filterChain.doFilter(req,resp);
return;
}
}
}
统计网站IP访问次数
需要一个全局的集合来放数据,而且这个集合在服务启动时创建;在监听器的初始化方法中创建一个map并放入application作用域中。
@WebListener()
public class Listener implements ServletContextListener{
// 定义存储集合
private LinkedHashMap<String,Integer> linkedHashMap;
public Listener() {
}
public void contextInitialized(ServletContextEvent sce) {
// 服务器启动时创建map
linkedHashMap=new LinkedHashMap<>();
ServletContext application = sce.getServletContext();
// 将map放入application作用域
application.setAttribute("map",linkedHashMap);
}
public void contextDestroyed(ServletContextEvent sce) {
}
}
@WebFilter(urlPatterns = "*.jsp")
public class Filter implements javax.servlet.Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 拦截统计次数,放行
ServletContext application = req.getServletContext();
LinkedHashMap map =(LinkedHashMap) application.getAttribute("map");
String ip = req.getLocalAddr();
Integer count =(Integer) map.get(ip);
if(count==null){
map.put(ip,1);
}else {
map.put(ip,count+1);
}
application.setAttribute("map",map);
System.out.println("被拦截");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri ="http://java.sun.com/jsp/jstl/core" %>>
<html>
<head>
<title>统计Ip访问次数</title>
</head>
<body>
<table align="center" width="60%" border="1">
<tr>
<td>IP</td>
<td>访问次数</td>
</tr>
<c:forEach items="${applicationScope.map}" var="entry">
<tr>
<td>${entry.key}</td>
<td>${entry.value}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
监听器
事件源:三大域
-
servletContext;与天地同寿
- 生死监听:ServletContextListener;有两个方法,一个在出生时调用,一个在死亡时调用。
- 属性监听:ServletContextAttributeListener;由三个方法,一个是在添加属性时调用,一个在替换属性时调用,最后一个在移除属性时调用。
-
HttpSession;有session时才创建
- 生死监听:HttpSessionListener;有两个方法,一个在出生时调用,一个在死亡时调用。
- 属性监听:HttpSessionAttributeListener;由三个方法,一个是在添加属性时调用,一个在替换属性时调用,最后一个在移除属性时调用。
-
ServletRequest;发出请求就创建
- 生死监听: ServletRequestListener;有两个方法,一个在出生时调用,一个在死亡时调用。
- 属性监听: ServletRequestAttributeListener;由三个方法,一个是在添加属性时调用,一个在替换属性时调用,最后一个在移除属性时调用。
Listener没有路径,特定事件发生时,会执行监听器代码;
- ServletRequestListener:在请求对象初始化和销毁时,会被调用;
- HttpSessionListener:在session初始化和销毁的时候会被调用;
事件对象
- ServletContextEvent:ServletContext.getServletContext();
- HTTPSessionEvent:HTTPSession.getSession();
- ServletRequest:
- ServletContext.getServletContext();
- ServletRequest.getServletRequest();
感知监听器
- 它用来添加JavaBean上,而不是添加到三大域上;
- 这两个监听器都不用需要在web.xml中注册;
意义:让对象知道自己是否被添加到session中。
应用场景:
-
ServletContextListener:在application对象创建和销毁的时候被调用;
-
ServletRequestAttributeListener:在往request作用域中变量发生变化时被调用
-
HTTPSessionAttributeListener:在往session作用域中变量发生变化时被调用
-
ServletContextAttributeListener:在往application作用域中变量发生变化时被调用
-
HttpSessionActivationListener:在session钝化(从内存到磁盘)和活化(从磁盘到内存)session中存储对象,
对象要实现序列化和反序列化; -
HTTPSessionBindingListener: