楼层: 首页/ 软件技术/ JavaEE / Jakarta EE/ 小项目实战:登录注册 + 留言板
10

小项目实战:登录注册 + 留言板

Mini Project · Servlet + JSP + JDBC + MySQL

把这一页的零件全拼起来:用户能注册、能登录、登录后能留言、能看别人留的言。数据库是 MySQL,表结构先建好,后面的代码围绕它转。这个项目跑通,你就理解了一个传统 Web 系统的骨架。

第一步:建库建表(MySQL 里执行)

CREATE DATABASE board_db CHARACTER SET utf8mb4;
USE board_db;

CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50) UNIQUE NOT NULL,
  password VARCHAR(100) NOT NULL     -- 生产环境存 BCrypt 哈希,别存明文!
);

CREATE TABLE messages (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_id INT NOT NULL,
  content VARCHAR(500),
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

第二步:注册 Servlet(RegisterServlet)

@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        String sql = "INSERT INTO users(username, password) VALUES(?, ?)";
        // 省略从 HikariCP 拿连接、PreparedStatement 绑定参数的样板——参考第 4、5 章
        // 成功后重定向到登录页;用户名重复(唯一索引冲突)就回注册页提示
        resp.sendRedirect("/login.jsp");
    }
}

第三步:登录 Servlet + 留言列表 Servlet

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        // SELECT * FROM users WHERE username=? AND password=? —— PreparedStatement
        boolean ok = /* 查库看有没有这一行 */;
        if (ok) {
            req.getSession().setAttribute("loginUser", username);  // 写 Session
            resp.sendRedirect("/board");                       // 跳留言板
        } else {
            req.setAttribute("error", "用户名或密码错");
            req.getRequestDispatcher("/login.jsp").forward(req, resp);
        }
    }
}

@WebServlet("/board")
public class BoardServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        // SELECT m.content, u.username, m.created_at
        // FROM messages m JOIN users u ON m.user_id = u.id ORDER BY m.created_at DESC
        List<String[]> rows = /* 查出来封装成 List */;
        req.setAttribute("rows", rows);
        req.getRequestDispatcher("/board.jsp").forward(req, resp);  // 交给 JSP 展示
    }
}

第四步:board.jsp 用 JSTL 把留言画出来

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://xmlns.jcp.org/jsp/jstl/core" %>
<h2>留言板(欢迎你,${loginUser})</h2>
<ul>
  <c:forEach items="${rows}" var="r">
    <li><b>${r[1]}</b> 说:${r[0]} <small>${r[2]}</small></li>
  </c:forEach>
</ul>
<form action="/post" method="post">
  <textarea name="content"></textarea>
  <button>发表</button>
</form>

第五步:发表留言 PostServlet(登录后才能用)

@WebServlet("/post")
public class PostServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        // 注意:LoginFilter 已经在 /admin/* 拦了未登录,
        // 这里从 Session 拿当前用户,别信表单里传的 user_id!
        String username = (String) req.getSession().getAttribute("loginUser");
        String content = req.getParameter("content");

        // INSERT INTO messages(user_id, content) VALUES(?, ?)
        // 用 PreparedStatement,content 里带什么引号都安全

        resp.sendRedirect("/board");   // 重定向回列表,避免刷新重复发表
    }
}

这个小项目的目录长这样(Maven 标准结构)

board/
├── pom.xml                     # 依赖:servlet-api、mysql 驱动、JSTL、HikariCP
└── src/main/
    ├── java/com/example/board/
    │   ├── RegisterServlet.java
    │   ├── LoginServlet.java
    │   ├── BoardServlet.java
    │   ├── PostServlet.java
    │   ├── EncodingFilter.java
    │   └── LoginFilter.java
    └── webapp/
        ├── login.jsp
        ├── register.jsp
        └── board.jsp

这个项目最常见的三个报错

报错八成是什么原因
ClassNotFoundException: DriverMySQL JDBC 驱动包没加进项目(Maven 依赖没导,或 lib 里没 jar)。
Access denied for user 'root'JDBC URL 里用户名密码写错,或 MySQL 没给这个账号权限。
登录了却跳回登录页Session 过期、换浏览器测了,或 LoginFilter 里取 Session 属性名和登录时塞的不一致。

论这个项目练到了哪些点

一张请求链路图:浏览器 → EncodingFilter(统一编码)→ LoginFilter(没登录拦回 login.jsp)→ BoardServlet(JDBC 查 messages 表)→ request 塞数据 → forward 给 board.jsp(JSTL 遍历)→ 浏览器看到留言列表。注册、登录用 Session 维持状态,PostServlet 把留言 INSERT 回数据库。

扩展作业:① 加 BCrypt 密码哈希(别存明文);② 留言内容用 c:out 输出防 XSS;③ 登出按钮 session.invalidate();④ 分页。做完这四样,这个小项目就能写进简历"项目经历"了。

章末面试 · 项目实战(3 题)

1.(设计题)PostServlet 里为什么从 Session 拿 user_id,而不是从表单读?

查看答案

答案:表单里的 user_id 用户能随便改,从 Session 拿才是真实登录用户。信 Session 不信表单。

2.(排错题)登录成功后又跳回登录页?

查看答案

答案:① LoginFilter 里取的 Session 属性名和登录时塞的不一致;② Session 过期;③ 换浏览器测了(Cookie 没带)。

3.(思考题)密码为什么不能明文存数据库?怎么存?

查看答案

答案:数据库一脱库用户全线裸奔。用 BCrypt/Argon2 哈希加盐,不可逆。验证时把输入哈希后和库里存的比。