实验:Servlet、Filter和Listener实现
实验5:Servlet、Filter和Listener实现用户登录验证与在线用户监控
我将为您完成这个实验,创建一个完整的Web应用程序,包含Servlet进行用户验证、Filter进行访问控制、Listener监控在线用户。
项目结构
/WebContent
/WEB-INF
/classes
/com/example
VerifyAction.java
UserFilter.java
SessionListener.java
Users.java
web.xml
/loginfolder
showinfo.html
showonlinename.jsp
index.jsp
1. index.jsp (登录页面)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<%
application.setAttribute("userdb", "admin_123#root_password#tom_123456");
out.print("合法的用户名和密码如下:"+"<br>");
String[] userinfo=((String)application.getAttribute("userdb")).split("#");
for(int i=0;i<userinfo.length;i++) {
out.print(userinfo[i]+"<br>");
}
%>
<h2>用户登录</h2>
<form action="verify" method="post">
用户名: <input type="text" name="user"><br>
密码: <input type="password" name="pass"><br>
<input type="submit" value="登录">
</form>
<a href="loginfolder/showinfo.html">不登录直接进入</a>
</body>
</html>
2. VerifyAction.java (Servlet)
package com.example;
import java.io.IOException;
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 javax.servlet.http.HttpSession;
@WebServlet("/verify")
public class VerifyAction extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
String user = request.getParameter("user");
String pass = request.getParameter("pass");
if(user != null && pass != null) {
String userdb = (String) request.getServletContext().getAttribute("userdb");
String[] userinfo = userdb.split("#");
for(String info : userinfo) {
if(info.equals(user + "_" + pass)) {
HttpSession session = request.getSession();
session.setAttribute("username", user);
response.sendRedirect("loginfolder/showinfo.html");
return;
}
}
}
response.getWriter().println("<br>错误的用户名或密码!!!!<br>");
response.getWriter().println("<a href='../index.jsp'>返回登录页面</a>");
}
}
3. UserFilter.java (过滤器)
package com.example;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebFilter("/loginfolder/*")
public class UserFilter implements Filter {
public void init(FilterConfig fConfig) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession(false);
if(session != null && session.getAttribute("username") != null) {
chain.doFilter(request, response);
} else {
res.sendRedirect("../index.jsp");
}
}
}
4. SessionListener.java (监听器)
package com.example;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class SessionListener implements HttpSessionAttributeListener, HttpSessionListener {
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
if("username".equals(event.getName())) {
Users.addUser(event.getValue().toString());
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
if("username".equals(event.getName())) {
Users.removeUser(event.getValue().toString());
}
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
if("username".equals(event.getName())) {
String oldUser = (String) event.getValue();
String newUser = (String) event.getSession().getAttribute("username");
Users.removeUser(oldUser);
Users.addUser(newUser);
}
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
String username = (String) se.getSession().getAttribute("username");
if(username != null) {
Users.removeUser(username);
}
}
@Override
public void sessionCreated(HttpSessionEvent se) {
// Session创建时设置超时时间为10秒
se.getSession().setMaxInactiveInterval(10);
}
}
5. Users.java (在线用户管理)
package com.example;
import java.util.Vector;
public class Users {
private static Vector<String> userList = new Vector<String>();
public static void addUser(String username) {
if(!userList.contains(username)) {
userList.add(username);
}
}
public static void removeUser(String username) {
userList.remove(username);
}
public static Vector<String> getList() {
return userList;
}
}
6. showinfo.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta http-equiv="Cache-Control" content="no-cache">
<title>信息页面</title>
</head>
<body>
<h1>登录成功!</h1>
<a href="showonlinename.jsp">查看当前在线用户</a>
</body>
</html>
7. showonlinename.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.example.Users" %>
<%@ page import="java.util.Vector" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta http-equiv="Cache-Control" content="no-cache">
<title>在线用户</title>
</head>
<body>
<%
request.setCharacterEncoding("utf8");
session.setMaxInactiveInterval(10);
out.print("当前在线人员:<br>");
Vector<String> vector = Users.getList();
if(vector != null && vector.size() > 0) {
for(int i=0; i<vector.size(); i++) {
out.println(vector.elementAt(i) + "<br>");
}
} else {
out.println("暂无在线用户<br>");
}
%>
<a href="../index.jsp">返回登录页面</a>
</body>
</html>
8. 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">
<display-name>Experiment5</display-name>
<filter>
<filter-name>UserFilter</filter-name>
<filter-class>com.example.UserFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UserFilter</filter-name>
<url-pattern>/loginfolder/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
打包说明
- 在Eclipse中右键项目 -> Export -> WAR file
- 确保包含Java源文件(勾选相应的选项)
- 将生成的WAR文件提交
测试说明
- 访问index.jsp页面,显示登录表单和合法用户列表
- 输入正确的用户名和密码(如admin/123)将跳转到showinfo.html
- 输入错误的用户名或密码将显示错误信息
- 不登录直接访问/loginfolder/下的任何资源将被重定向回登录页面
- 登录后可以查看在线用户列表
- 10秒不操作后session将过期,用户将从在线列表中移除
这个实现满足了实验的所有要求,包括Servlet验证、Filter过滤、Listener监听session事件,并使用Vector管理在线用户列表。所有文件都使用UTF-8编码,并包含禁用缓存的设置。
1.Servlet是干嘛的
UserFilter.java 是一个 过滤器(Filter),它的核心作用是 拦截特定请求,检查用户是否已登录。如果没有登录,则强制跳转回登录页面(index.jsp),确保受保护的资源(如 loginfolder/ 下的文件)只能被已登录用户访问。
过滤器的作用(类比现实场景)
想象一个电影院检票员:
- 你要进影厅(访问
loginfolder/showinfo.html) - 检票员(Filter)拦住你:
- 如果你有票(
session中有username)→ 放行(chain.doFilter())。 - 如果没票(未登录)→ 赶你去买票(重定向到
index.jsp)。
- 如果你有票(
在实验中的具体功能
-
拦截路径:
- 配置为过滤
/loginfolder/*(通过@WebFilter注解)。 - 所有访问
loginfolder目录下资源的请求(如showinfo.html、showonlinename.jsp)都会先经过过滤器。
- 配置为过滤
-
检查登录状态:
- 从
session中检查是否存在username属性(即用户是否通过VerifyActionServlet 成功登录)。 - 如果存在 → 放行,允许访问目标资源。
- 如果不存在 → 重定向到登录页面。
- 从
-
防止绕过登录:
- 直接访问
http://.../loginfolder/showinfo.html会被拦截,确保安全。
- 直接访问
关键代码解析
@WebFilter("/loginfolder/*") // 只过滤/loginfolder/下的请求
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 1. 强制转换为 HttpServletRequest(才能获取 session)
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 2. 获取 session(注意:false 表示不自动创建新 session)
HttpSession session = req.getSession(false);
// 3. 检查是否登录
if (session != null && session.getAttribute("username") != null) {
// 已登录 → 放行,继续执行后续操作(如访问 showinfo.html)
chain.doFilter(request, response);
} else {
// 未登录 → 重定向到首页
res.sendRedirect("../index.jsp");
}
}
为什么需要过滤器?
- 集中控制权限:
- 无需在每个 JSP 或 Servlet 中重复编写登录检查代码。
- 保护敏感资源:
- 防止用户直接输入 URL 访问受限页面(如后台管理页)。
- 灵活配置:
- 可通过
web.xml或注解(@WebFilter)指定过滤的路径。
- 可通过
实验中的协作流程
- 用户登录 →
VerifyActionServlet 验证成功,将用户名存入session。 - 用户访问
/loginfolder/showinfo.html→ 被UserFilter拦截:- 如果
session中有username→ 显示showinfo.html。 - 如果无
username→ 跳回index.jsp。
- 如果
- 用户点击“不登录直接进入” → 被过滤器拦截,无法访问。
对比 Servlet 和 Filter
| Servlet | Filter |
|---|---|
处理具体请求(如 /verify) |
拦截匹配的请求(如 /loginfolder/*) |
| 生成响应(HTML/重定向) | 决定是否放行(chain.doFilter())或拦截 |
| 通常用于业务逻辑 | 通常用于权限控制、日志记录、编码设置等 |
常见问题
-
为什么用
req.getSession(false)?- 参数
false表示“如果不存在 session,则返回null”,避免自动创建无用的 session。
- 参数
-
如何排除某些路径不过滤?
- 调整
@WebFilter的路径规则(如/loginfolder/secure/*)。
- 调整
-
过滤器能修改请求/响应吗?
- 可以!例如对请求统一设置 UTF-8 编码,或对响应添加头部信息。
总结
UserFilter是实验中的安全守卫,确保只有登录用户能访问受限资源。- 它通过检查
session实现简单的权限控制,是 Web 开发中常用的技术。
2.UserFilter.java (过滤器)干嘛的
UserFilter.java 是一个 过滤器(Filter),它的核心作用是 拦截特定请求,检查用户是否已登录。如果没有登录,则强制跳转回登录页面(index.jsp),确保受保护的资源(如 loginfolder/ 下的文件)只能被已登录用户访问。
过滤器的作用(类比现实场景)
想象一个电影院检票员:
- 你要进影厅(访问
loginfolder/showinfo.html) - 检票员(Filter)拦住你:
- 如果你有票(
session中有username)→ 放行(chain.doFilter())。 - 如果没票(未登录)→ 赶你去买票(重定向到
index.jsp)。
- 如果你有票(
在实验中的具体功能
-
拦截路径:
- 配置为过滤
/loginfolder/*(通过@WebFilter注解)。 - 所有访问
loginfolder目录下资源的请求(如showinfo.html、showonlinename.jsp)都会先经过过滤器。
- 配置为过滤
-
检查登录状态:
- 从
session中检查是否存在username属性(即用户是否通过VerifyActionServlet 成功登录)。 - 如果存在 → 放行,允许访问目标资源。
- 如果不存在 → 重定向到登录页面。
- 从
-
防止绕过登录:
- 直接访问
http://.../loginfolder/showinfo.html会被拦截,确保安全。
- 直接访问
关键代码解析
@WebFilter("/loginfolder/*") // 只过滤/loginfolder/下的请求
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 1. 强制转换为 HttpServletRequest(才能获取 session)
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 2. 获取 session(注意:false 表示不自动创建新 session)
HttpSession session = req.getSession(false);
// 3. 检查是否登录
if (session != null && session.getAttribute("username") != null) {
// 已登录 → 放行,继续执行后续操作(如访问 showinfo.html)
chain.doFilter(request, response);
} else {
// 未登录 → 重定向到首页
res.sendRedirect("../index.jsp");
}
}
为什么需要过滤器?
- 集中控制权限:
- 无需在每个 JSP 或 Servlet 中重复编写登录检查代码。
- 保护敏感资源:
- 防止用户直接输入 URL 访问受限页面(如后台管理页)。
- 灵活配置:
- 可通过
web.xml或注解(@WebFilter)指定过滤的路径。
- 可通过
实验中的协作流程
- 用户登录 →
VerifyActionServlet 验证成功,将用户名存入session。 - 用户访问
/loginfolder/showinfo.html→ 被UserFilter拦截:- 如果
session中有username→ 显示showinfo.html。 - 如果无
username→ 跳回index.jsp。
- 如果
- 用户点击“不登录直接进入” → 被过滤器拦截,无法访问。
对比 Servlet 和 Filter
| Servlet | Filter |
|---|---|
处理具体请求(如 /verify) |
拦截匹配的请求(如 /loginfolder/*) |
| 生成响应(HTML/重定向) | 决定是否放行(chain.doFilter())或拦截 |
| 通常用于业务逻辑 | 通常用于权限控制、日志记录、编码设置等 |
常见问题
-
为什么用
req.getSession(false)?- 参数
false表示“如果不存在 session,则返回null”,避免自动创建无用的 session。
- 参数
-
如何排除某些路径不过滤?
- 调整
@WebFilter的路径规则(如/loginfolder/secure/*)。
- 调整
-
过滤器能修改请求/响应吗?
- 可以!例如对请求统一设置 UTF-8 编码,或对响应添加头部信息。
总结
UserFilter是实验中的安全守卫,确保只有登录用户能访问受限资源。- 它通过检查
session实现简单的权限控制,是 Web 开发中常用的技术。
3.SessionListener.java (监听器)是干嘛的
SessionListener.java 是一个 监听器(Listener),它的作用是 实时监控用户会话(Session)的状态变化,并根据这些变化动态维护一个 在线用户列表。在您的实验中,它主要做了两件事:
- 监听 Session 中用户名的变化(添加、替换、删除)。
- 更新在线用户列表(
Users类中的静态Vector)。
监听器的核心功能(类比现实场景)
想象一个 会议室签到系统:
- 有人签到(
session.setAttribute("username", "tom")) → 监听器将名字加入名单。 - 有人修改签到名(替换
username) → 监听器更新名单。 - 有人离开或超时(
session过期) → 监听器从名单中移除名字。
在实验中的具体作用
-
用户登录成功时:
VerifyActionServlet 将用户名存入 Session(session.setAttribute("username", "admin"))。- 监听器检测到
username属性被添加 → 调用Users.addUser("admin"),将用户加入在线列表。
-
用户退出或 Session 过期时:
- Session 超时(默认 10 秒)或被手动销毁 → 监听器自动调用
Users.removeUser(),从在线列表中移除用户。
- Session 超时(默认 10 秒)或被手动销毁 → 监听器自动调用
-
显示在线用户时:
showonlinename.jsp从Users.getList()获取当前在线用户列表并显示。
关键代码解析
@WebListener
public class SessionListener implements HttpSessionAttributeListener, HttpSessionListener {
// 监听 Session 属性添加(用户登录)
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
if ("username".equals(event.getName())) {
Users.addUser(event.getValue().toString()); // 将用户加入在线列表
}
}
// 监听 Session 属性移除(用户退出)
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
if ("username".equals(event.getName())) {
Users.removeUser(event.getValue().toString()); // 从在线列表移除
}
}
// 监听 Session 超时或销毁
@Override
public void sessionDestroyed(HttpSessionEvent se) {
String username = (String) se.getSession().getAttribute("username");
if (username != null) {
Users.removeUser(username); // 确保用户被移除
}
}
// 创建 Session 时设置超时时间(10秒)
@Override
public void sessionCreated(HttpSessionEvent se) {
se.getSession().setMaxInactiveInterval(10);
}
}
为什么需要监听器?
- 自动化管理在线状态:
- 无需手动在 Servlet 或 JSP 中调用
addUser/removeUser,监听器自动处理。
- 无需手动在 Servlet 或 JSP 中调用
- 保证数据一致性:
- 即使用户直接关闭浏览器(未点击退出),Session 超时后监听器仍会清理在线列表。
- 低耦合设计:
- 监听器与业务逻辑分离,便于扩展(如未来添加日志记录功能)。
监听器与 Servlet/Filter 的协作流程
- 用户登录 →
VerifyAction设置session.setAttribute("username", ...)→ 触发监听器的attributeAdded。 - 用户访问在线列表 →
showonlinename.jsp从Users.getList()读取数据。 - 用户超时未操作 → Session 过期 → 触发监听器的
sessionDestroyed→ 清理在线列表。
监听器的常见用途
- 在线用户统计(如实验中的功能)。
- 资源清理:Session 过期时释放数据库连接等资源。
- 行为审计:记录用户的登录/退出时间。
实验中的注意事项
- Session 超时时间:
- 代码中设置为 10 秒(
setMaxInactiveInterval(10)),但实际可能因服务器配置略有延迟。
- 代码中设置为 10 秒(
- 线程安全:
Users类使用Vector(线程安全的集合),避免多用户同时操作列表导致数据错乱。
- 测试方法:
- 用不同浏览器登录两个账号,观察
showonlinename.jsp是否实时更新。
- 用不同浏览器登录两个账号,观察
总结
SessionListener是实验中的 “隐形管理员”,默默跟踪用户登录状态,维护在线列表。- 它通过监听 Session 的生命周期事件(创建、销毁、属性变化),确保
Users列表始终准确。 - 这种设计符合 观察者模式,是 Java Web 开发中解耦逻辑的经典实践。

浙公网安备 33010602011771号