Servlet-从零构建用户认证系统:登录与注册实战
2026/6/30 10:36:27 网站建设 项目流程

1. 环境准备与项目搭建

第一次用Servlet做用户认证系统时,我踩过不少坑。记得当时连数据库连接池都配了半天,页面提交的数据死活传不到后端。现在回头看,其实只要把环境搭对了,后面都是水到渠成的事。咱们就从最基础的Maven项目开始,手把手带你避开那些新手必踩的雷区。

1.1 项目骨架搭建

打开IDEA新建Maven项目时,千万别勾选"Create from archetype"。我见过太多新手被骨架项目里那些自动生成的复杂目录搞晕。咱们就用最干净的空白项目,自己建目录结构更清晰。具体操作:

  1. File -> New -> Project
  2. 左侧选Maven
  3. 不勾选任何模板
  4. 输入GroupId(比如com.yourname)
  5. 输入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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这里特别提醒三点:

  1. 密码字段长度至少100,为后续加密留空间
  2. 一定要加UNIQUE约束防止重复用户名
  3. 使用utf8mb4字符集避免emoji存储问题

1.3 依赖配置的坑

pom.xml里这几个依赖版本是我实测最稳定的组合:

<dependencies> <!-- Servlet API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- MyBatis核心 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> </dependencies>

特别注意:MySQL 8.0+的驱动类名变了,配置文件中要写com.mysql.cj.jdbc.Driver而不是老版本的com.mysql.jdbc.Driver,这个错误我排查过整整一下午。

2. 登录功能深度实现

2.1 密码安全处理

新手最常犯的错误就是明文存储密码。咱们用最简单的MD5加密起步(实际项目建议用BCrypt):

public 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> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency>

配置文件中修改:

<dataSource type="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db1"/> <property name="username" value="root"/> <property name="password" value="123456"/> <!-- 连接池参数 --> <property name="maxActive" value="20"/> <property name="initialSize" value="5"/> </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?error=1"); } else { resp.sendRedirect("500.html"); } } }

在web.xml中配置:

<error-page> <exception-type>java.lang.Exception</exception-type> <location>/errorHandler</location> </error-page>

这样用户看到的是友好的错误提示,而不是Tomcat的报错页面。记得在开发阶段可以暂时关闭这个功能,方便调试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询