开启你的编程学习之旅

云课堂提供高质量的编程课程,从入门到精通,助你成为技术大牛

立即开始学习

Java Web 开发实战

作者: 钱老师 更新: 2024-03-20 阅读: 42367 难度: 中级
学习工具

5. Servlet用户登录功能实现

用户登录是 Web 应用中最基本的功能之一。本章将通过一个完整的登录功能实现,深入讲解 Servlet 如何处理表单数据、会话管理和页面跳转。

登录功能 Servlet 实现

下面是一个完整的用户登录 Servlet 实现:

LoginServlet.java
import java.io.*; import javax.servlet.*; import javax.servlet.annotation.*; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override public void init() throws ServletException { // Servlet 初始化代码 System.out.println("LoginServlet 初始化"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 如果是GET请求,直接重定向到登录页面 response.sendRedirect("login.jsp"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置字符编码,防止中文乱码 request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); // 获取请求参数 String username = request.getParameter("username"); String password = request.getParameter("password"); if ("admin".equals(username) && "123456".equals(password)) { // 登录成功,设置session HttpSession session = request.getSession(); session.setAttribute("username", username); session.setAttribute("loginTime", new java.util.Date()); // 重定向到欢迎页面 response.sendRedirect("welcome.jsp"); } else { // 登录失败,重定向回登录页面并携带错误参数 response.sendRedirect("login.jsp?error=true"); } } @Override public void destroy() { // Servlet 销毁代码 System.out.println("LoginServlet 销毁"); } }

登录页面 JSP 实现

配合登录 Servlet 的 JSP 页面:

login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>用户登录</title> <style> body { font-family: Arial, sans-serif; background-color: #f5f5f5; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; } .login-container { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 300px; } .form-group { margin-bottom: 20px; } label { display: block; margin-bottom: 5px; font-weight: bold; } input[type="text"], input[type="password"] { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; } .btn-login { width: 100%; padding: 10px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } .btn-login:hover { background-color: #0056b3; } .error-message { color: #dc3545; background-color: #f8d7da; border: 1px solid #f5c6cb; padding: 10px; border-radius: 4px; margin-bottom: 20px; display: none; } .show-error { display: block; } </style> </head> <body> <div class="login-container"> <h2 style="text-align: center; margin-bottom: 30px;">用户登录</h2> <div id="errorMessage" class="error-message"> 用户名或密码错误! </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" class="btn-login">登录</button> </form> <div style="margin-top: 20px; text-align: center; font-size: 12px; color: #666;"> <p>测试账号:admin</p> <p>测试密码:123456</p> </div> </div> <script> // 检查URL参数,显示错误消息 const urlParams = new URLSearchParams(window.location.search); if (urlParams.get('error') === 'true') { document.getElementById('errorMessage').classList.add('show-error'); } // 自动隐藏错误消息 setTimeout(function() { const errorDiv = document.getElementById('errorMessage'); if (errorDiv) { errorDiv.classList.remove('show-error'); } }, 5000); </script> </body> </html>

欢迎页面 JSP 实现

登录成功后的欢迎页面:

welcome.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.text.SimpleDateFormat" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>欢迎页面</title> <style> body { font-family: Arial, sans-serif; background-color: #f8f9fa; margin: 0; padding: 20px; } .welcome-container { max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .user-info { background-color: #e9f7ef; border: 1px solid #d1ecf1; padding: 20px; border-radius: 4px; margin-bottom: 20px; } .btn-logout { background-color: #dc3545; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; } .btn-logout:hover { background-color: #c82333; } .nav-links { margin-top: 20px; } .nav-links a { margin-right: 15px; text-decoration: none; color: #007bff; } </style> </head> <body> <div class="welcome-container"> <h1>欢迎来到系统</h1> <% // 检查用户是否登录 String username = (String) session.getAttribute("username"); java.util.Date loginTime = (java.util.Date) session.getAttribute("loginTime"); if (username == null) { // 用户未登录,重定向到登录页面 response.sendRedirect("login.jsp"); return; } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); %> <div class="user-info"> <h2>欢迎,<%= username %>!</h2> <p><strong>登录时间:</strong> <%= sdf.format(loginTime) %></p> <p><strong>当前时间:</strong> <%= sdf.format(new java.util.Date()) %></p> <p><strong>会话ID:</strong> <%= session.getId() %></p> </div> <div class="nav-links"> <h3>功能菜单</h3> <ul> <li><a href="profile.jsp">个人资料</a></li> <li><a href="settings.jsp">系统设置</a></li> <li><a href="reports.jsp">数据报表</a></li> </ul> </div> <div style="margin-top: 30px;"> <a href="logout" class="btn-logout">退出登录</a> </div> </div> </body> </html>

退出登录 Servlet

处理用户退出登录的功能:

LogoutServlet.java
import java.io.*; import javax.servlet.*; import javax.servlet.annotation.*; import javax.servlet.http.*; @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) { // 记录退出日志 String username = (String) session.getAttribute("username"); System.out.println("用户 " + username + " 退出登录"); // 使会话失效 session.invalidate(); } // 重定向到登录页面 response.sendRedirect("login.jsp"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

登录过滤器

用于检查用户访问权限的过滤器:

AuthFilter.java
import java.io.*; import javax.servlet.*; import javax.servlet.annotation.*; import javax.servlet.http.*; @WebFilter({"/*"}) public class AuthFilter implements Filter { // 不需要登录就可以访问的路径 private static final String[] ALLOWED_PATHS = { "/login", "/login.jsp", "/logout", "/css/", "/js/", "/images/" }; @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("AuthFilter 初始化"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String path = httpRequest.getRequestURI().substring( httpRequest.getContextPath().length()); // 检查当前路径是否在允许列表中 boolean allowed = false; for (String allowedPath : ALLOWED_PATHS) { if (path.startsWith(allowedPath)) { allowed = true; break; } } // 如果路径不在允许列表中,检查用户是否已登录 if (!allowed) { HttpSession session = httpRequest.getSession(false); if (session == null || session.getAttribute("username") == null) { // 用户未登录,重定向到登录页面 httpResponse.sendRedirect(httpRequest.getContextPath() + "/login.jsp"); return; } } // 继续处理请求 chain.doFilter(request, response); } @Override public void destroy() { System.out.println("AuthFilter 销毁"); } }

增强版登录 Servlet

包含更多功能的登录 Servlet:

EnhancedLoginServlet.java
import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.annotation.*; import javax.servlet.http.*; @WebServlet("/enhanced-login") public class EnhancedLoginServlet extends HttpServlet { // 模拟用户数据库 private static final Map<String, String> USER_DB = new HashMap<>(); private static final Map<String, Integer> LOGIN_ATTEMPTS = new HashMap<>(); @Override public void init() throws ServletException { // 初始化测试用户 USER_DB.put("admin", "123456"); USER_DB.put("user1", "password1"); USER_DB.put("user2", "password2"); System.out.println("EnhancedLoginServlet 初始化完成"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); String rememberMe = request.getParameter("rememberMe"); // 检查登录尝试次数 if (isAccountLocked(username)) { response.sendRedirect("login.jsp?error=locked"); return; } // 验证用户凭据 if (authenticate(username, password)) { // 登录成功 resetLoginAttempts(username); HttpSession session = request.getSession(); session.setAttribute("username", username); session.setAttribute("loginTime", new java.util.Date()); session.setAttribute("userAgent", request.getHeader("User-Agent")); // 设置记住我功能 if ("on".equals(rememberMe)) { Cookie userCookie = new Cookie("rememberedUser", username); userCookie.setMaxAge(30 * 24 * 60 * 60); // 30天 response.addCookie(userCookie); } // 记录登录日志 logLogin(username, request.getRemoteAddr()); // 重定向到原始请求页面或欢迎页面 String redirectUrl = request.getParameter("redirect"); if (redirectUrl != null && !redirectUrl.isEmpty()) { response.sendRedirect(redirectUrl); } else { response.sendRedirect("welcome.jsp"); } } else { // 登录失败 recordFailedAttempt(username); String errorType = "invalid"; if (isAccountLocked(username)) { errorType = "locked"; } response.sendRedirect("login.jsp?error=" + errorType); } } private boolean authenticate(String username, String password) { if (username == null || password == null) { return false; } String storedPassword = USER_DB.get(username); return storedPassword != null && storedPassword.equals(password); } private void recordFailedAttempt(String username) { if (username != null) { int attempts = LOGIN_ATTEMPTS.getOrDefault(username, 0) + 1; LOGIN_ATTEMPTS.put(username, attempts); System.out.println("用户 " + username + " 登录失败,尝试次数: " + attempts); } } private void resetLoginAttempts(String username) { if (username != null) { LOGIN_ATTEMPTS.remove(username); } } private boolean isAccountLocked(String username) { if (username == null) { return false; } Integer attempts = LOGIN_ATTEMPTS.get(username); return attempts != null && attempts >= 5; // 5次失败后锁定 } private void logLogin(String username, String ipAddress) { System.out.println(String.format("用户登录 - 用户名: %s, IP地址: %s, 时间: %s", username, ipAddress, new java.util.Date())); } }

关键知识点总结

  • 会话管理:使用 HttpSession 跟踪用户登录状态
  • 表单处理:通过 request.getParameter() 获取表单数据
  • 页面跳转:使用 response.sendRedirect() 进行重定向
  • 权限控制:通过过滤器实现统一的登录检查
  • 错误处理:通过 URL 参数传递错误信息
  • 安全措施:登录尝试次数限制、会话超时控制

最佳实践

  1. 始终对用户输入进行验证和清理
  2. 使用 HTTPS 保护登录数据传输
  3. 设置合理的会话超时时间
  4. 记录登录日志用于安全审计
  5. 实现登录失败次数限制防止暴力破解
  6. 使用密码哈希存储,不要在代码中硬编码密码
  7. 考虑实现验证码功能增强安全性

注意:在实际生产环境中,应该使用数据库存储用户信息,并对密码进行加密存储。硬编码的用户名密码仅用于演示目的。

提示: 这是一个重要的概念,需要特别注意理解和掌握。
注意: 这是一个常见的错误点,请避免犯同样的错误。