Servlet-从零构建用户认证系统:登录与注册实战

📅 2026/6/30 10:36:32
Servlet-从零构建用户认证系统:登录与注册实战
1. 环境准备与项目搭建第一次用Servlet做用户认证系统时我踩过不少坑。记得当时连数据库连接池都配了半天页面提交的数据死活传不到后端。现在回头看其实只要把环境搭对了后面都是水到渠成的事。咱们就从最基础的Maven项目开始手把手带你避开那些新手必踩的雷区。1.1 项目骨架搭建打开IDEA新建Maven项目时千万别勾选Create from archetype。我见过太多新手被骨架项目里那些自动生成的复杂目录搞晕。咱们就用最干净的空白项目自己建目录结构更清晰。具体操作File - New - Project左侧选Maven不勾选任何模板输入GroupId比如com.yourname输入ArtifactId比如auth-system建好后别急着写代码先右键项目名 - Add Framework Support - 勾选Web Application。这一步会自动生成webapp目录你的HTML和CSS将来就放在这里。我建议立即在webapp下新建css和js文件夹养成前端资源分类存放的好习惯。1.2 数据库设计要点用户表设计看似简单但有几个关键点新手容易忽略CREATE TABLE tb_user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(20) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, -- 注意长度要给够 create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里特别提醒三点密码字段长度至少100为后续加密留空间一定要加UNIQUE约束防止重复用户名使用utf8mb4字符集避免emoji存储问题1.3 依赖配置的坑pom.xml里这几个依赖版本是我实测最稳定的组合dependencies !-- Servlet API -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- MyBatis核心 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.6/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.25/version /dependency /dependencies特别注意MySQL 8.0的驱动类名变了配置文件中要写com.mysql.cj.jdbc.Driver而不是老版本的com.mysql.jdbc.Driver这个错误我排查过整整一下午。2. 登录功能深度实现2.1 密码安全处理新手最常犯的错误就是明文存储密码。咱们用最简单的MD5加密起步实际项目建议用BCryptpublic class PasswordUtil { public static String encrypt(String password) { try { MessageDigest md MessageDigest.getInstance(MD5); byte[] hash md.digest(password.getBytes(StandardCharsets.UTF_8)); return Hex.getHexString(hash); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(加密失败, e); } } }在Servlet里调用时记得先加密再比对String encryptedPwd PasswordUtil.encrypt(password); User user userMapper.login(username, encryptedPwd);2.2 会话管理技巧登录成功后不返回简单字符串而是建立会话HttpSession session request.getSession(); session.setAttribute(currentUser, user); response.sendRedirect(welcome.jsp); // 跳转到欢迎页这样其他页面就能通过session.getAttribute(currentUser)判断用户是否登录。我在早期项目中曾用Cookie实现结果被CSRF攻击教做人还是服务器端Session更安全。2.3 防暴力破解给登录接口加上简单的频率限制Integer attemptCount (Integer) session.getAttribute(loginAttempt); if (attemptCount null) attemptCount 0; if (attemptCount 3) { response.getWriter().write(尝试次数过多请5分钟后再试); return; } session.setAttribute(loginAttempt, attemptCount 1);虽然简陋但能防住最基本的暴力破解。我第一个上线的项目就因为这个疏忽被刷了几万次登录请求。3. 注册功能进阶实现3.1 输入验证策略前端验证永远不可靠后端必须做二次校验public boolean validateUser(User user) { if (user.getUsername() null || user.getUsername().length() 4) { return false; } // 密码强度校验 String passwordPattern ^(?.*[0-9])(?.*[a-z])(?.*[A-Z]).{8,}$; return Pattern.matches(passwordPattern, user.getPassword()); }建议用正则表达式校验密码强度我常用的规则是至少8位包含大小写字母和数字。曾经有用户注册时用123456当密码结果账号被盗反过来投诉系统不安全...3.2 事务处理要点注册操作要保证原子性try (SqlSession sqlSession sqlSessionFactory.openSession()) { UserMapper mapper sqlSession.getMapper(UserMapper.class); if (mapper.selectByUsername(user.getUsername()) ! null) { throw new RuntimeException(用户名已存在); } mapper.insert(user); // 初始化用户配置 initUserProfile(user.getId()); sqlSession.commit(); }这里容易踩的坑是忘记commit()我遇到过测试环境正常但生产环境数据不落库的情况就是因为没提交事务。4. 架构优化实战4.1 三层架构重构初期把所有逻辑都写在Servlet里是常见错误。建议分三层src/ ├── main/ │ ├── java/ │ │ ├── controller/ (Servlet) │ │ ├── service/ (业务逻辑) │ │ ├── dao/ (数据访问) │ │ └── model/ (实体类) │ └── webapp/ (前端资源)以登录为例Servlet只负责参数接收和结果返回WebServlet(/login) public class LoginController extends HttpServlet { private UserService userService new UserService(); protected void doPost(HttpServletRequest req, HttpServletResponse resp) { String username req.getParameter(username); String password req.getParameter(password); boolean success userService.login(username, password); // 返回JSON响应 } }4.2 连接池配置用Druid连接池替代MyBatis默认连接池!-- pom.xml新增 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency配置文件中修改dataSource typecom.alibaba.druid.pool.DruidDataSource property namedriverClassName valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/db1/ property nameusername valueroot/ property namepassword value123456/ !-- 连接池参数 -- property namemaxActive value20/ property nameinitialSize value5/ /dataSource这个优化让我的项目QPS从50提升到了300效果立竿见影。4.3 异常统一处理定义全局异常处理器WebServlet(/errorHandler) public class ErrorHandler extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) { Throwable exception (Throwable) req.getAttribute(javax.servlet.error.exception); // 根据异常类型返回不同错误页面 if (exception instanceof AuthenticationException) { resp.sendRedirect(login.html?error1); } else { resp.sendRedirect(500.html); } } }在web.xml中配置error-page exception-typejava.lang.Exception/exception-type location/errorHandler/location /error-page这样用户看到的是友好的错误提示而不是Tomcat的报错页面。记得在开发阶段可以暂时关闭这个功能方便调试。