本文目录导读:

我来提供一个完整的Java会话管理案例,包括登录、会话跟踪和登出功能。
Servlet会话管理示例
项目结构
session-management/
├── src/
│ └── com/example/
│ ├── LoginServlet.java
│ ├── LogoutServlet.java
│ ├── DashboardServlet.java
│ └── SessionListener.java
└── web/
└── WEB-INF/
└── web.xml
LoginServlet.java
package com.example;
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;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
// 模拟用户数据库
private static final String VALID_USERNAME = "admin";
private static final String VALID_PASSWORD = "password123";
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
// 验证用户凭据
if (VALID_USERNAME.equals(username) && VALID_PASSWORD.equals(password)) {
// 获取或创建会话
HttpSession session = request.getSession(true);
// 设置会话属性
session.setAttribute("username", username);
session.setAttribute("loginTime", System.currentTimeMillis());
session.setAttribute("role", "USER");
// 设置会话超时时间(秒)
session.setMaxInactiveInterval(30 * 60); // 30分钟
// 重定向到仪表板
response.sendRedirect("dashboard");
} else {
// 登录失败
request.setAttribute("errorMessage", "用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 显示登录页面
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
DashboardServlet.java
package com.example;
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;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/dashboard")
public class DashboardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取会话
HttpSession session = request.getSession(false);
// 检查用户是否已登录
if (session == null || session.getAttribute("username") == null) {
// 未登录,重定向到登录页面
response.sendRedirect("login");
return;
}
// 用户已登录,显示仪表板
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String username = (String) session.getAttribute("username");
long loginTime = (long) session.getAttribute("loginTime");
String role = (String) session.getAttribute("role");
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>用户仪表板</title>");
out.println("<style>");
out.println("body { font-family: Arial, sans-serif; margin: 40px; }");
out.println(".session-info { background: #f0f0f0; padding: 20px; border-radius: 5px; }");
out.println("</style>");
out.println("</head>");
out.println("<body>");
out.println("<h1>欢迎," + username + "!</h1>");
out.println("<div class='session-info'>");
out.println("<h2>会话信息</h2>");
out.println("<p>用户名: " + username + "</p>");
out.println("<p>角色: " + role + "</p>");
out.println("<p>登录时间: " + new java.util.Date(loginTime) + "</p>");
out.println("<p>会话ID: " + session.getId() + "</p>");
out.println("<p>会话超时时间: " + session.getMaxInactiveInterval() + " 秒</p>");
out.println("</div>");
out.println("<br>");
out.println("<a href='logout'>退出登录</a>");
out.println("</body>");
out.println("</html>");
}
}
LogoutServlet.java
package com.example;
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;
import java.io.IOException;
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取会话(不创建新会话)
HttpSession session = request.getSession(false);
if (session != null) {
// 清除所有会话属性
session.invalidate(); // 使会话失效
}
// 重定向到登录页面
response.sendRedirect("login");
}
}
SessionListener.java
package com.example;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class SessionListener implements HttpSessionListener {
private static int activeSessions = 0;
@Override
public void sessionCreated(HttpSessionEvent se) {
activeSessions++;
System.out.println("会话创建 - 当前活跃会话数: " + activeSessions);
System.out.println("会话ID: " + se.getSession().getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
activeSessions--;
System.out.println("会话销毁 - 当前活跃会话数: " + activeSessions);
System.out.println("会话ID: " + se.getSession().getId());
// 清理会话相关的资源
HttpSession session = se.getSession();
String username = (String) session.getAttribute("username");
if (username != null) {
System.out.println("用户 " + username + " 已登出");
}
}
public static int getActiveSessions() {
return activeSessions;
}
}
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_3_1.xsd"
version="3.1">
<display-name>Session Management Demo</display-name>
<!-- 全局会话超时设置(分钟) -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- 欢迎页面 -->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
登录页面 (login.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">用户登录</title>
<style>
body { font-family: Arial, sans-serif; margin: 50px; }
.login-form {
width: 300px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
}
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; }
input[type="text"], input[type="password"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
.error { color: red; margin-bottom: 10px; }
button {
background: #4CAF50;
color: white;
padding: 10px 15px;
border: none;
cursor: pointer;
width: 100%;
}
button:hover { background: #45a049; }
</style>
</head>
<body>
<div class="login-form">
<h2>用户登录</h2>
<% if(request.getAttribute("errorMessage") != null) { %>
<div class="error"><%= request.getAttribute("errorMessage") %></div>
<% } %>
<form action="login" method="post">
<div class="form-group">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit">登录</button>
</form>
<p style="text-align: center; margin-top: 15px;">
默认账号: admin / password123
</p>
</div>
</body>
</html>
使用Spring Boot的会话管理
Spring Boot应用配置
// 在application.properties中配置 server.servlet.session.timeout=30m server.servlet.session.cookie.name=MYAPP_SESSIONID server.servlet.session.cookie.http-only=true server.servlet.session.cookie.secure=true server.servlet.session.tracking-modes=cookie
Spring Security配置示例
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.session.HttpSessionEventPublisher;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login", "/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.sessionManagement()
.maximumSessions(1) // 每个用户最多一个会话
.maxSessionsPreventsLogin(false) // 允许新登录,使旧会话失效
.expiredUrl("/login?expired");
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
关键会话管理最佳实践
安全建议
// 1. 创建会话后的安全设置
public static void secureSession(HttpSession session) {
// 生成新的会话ID(防止会话固定攻击)
session.invalidate(); // 使旧会话失效
session = session.getRequest().getSession(true); // 创建新会话
// 设置会话属性
session.setAttribute("user", user);
session.setAttribute("ipAddress", request.getRemoteAddr());
session.setAttribute("userAgent", request.getHeader("User-Agent"));
}
// 2. 验证会话上下文
public static boolean validateSession(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) return false;
String storedIp = (String) session.getAttribute("ipAddress");
String currentIp = request.getRemoteAddr();
String storedAgent = (String) session.getAttribute("userAgent");
String currentAgent = request.getHeader("User-Agent");
// 验证IP和User-Agent是否变化
return storedIp != null && storedIp.equals(currentIp) &&
storedAgent != null && storedAgent.equals(currentAgent);
}
// 3. 会话失效处理
public static void invalidateSession(HttpSession session) {
if (session != null) {
// 清除所有属性
Enumeration<String> attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
String attributeName = attributeNames.nextElement();
session.removeAttribute(attributeName);
}
// 使会话失效
session.invalidate();
}
}
这个示例展示了Java中会话管理的核心概念,包括:
- 创建和销毁会话
- 会话属性管理
- 会话超时控制
- 会话监听器
- 安全最佳实践
- Spring Boot集成示例