Java Web双角色图书系统:含完整源码、MySQL建库脚本、Bootstrap前端与管理员/用户全流程操作演示
本文还有配套的精品资源,点击获取
简介:基于JDK 1.8和Tomcat 6.0–9.0运行的Java Web图书借阅系统,采用标准MVC架构(JSP+Servlet+JavaBean),支持普通用户与管理员两类角色独立登录与操作。普通用户可完成账号注册、登录、图书关键词检索、在线借书/还书、个人资料查看与修改;管理员拥有更高权限,包括用户信息管理(增删改查)、图书信息维护(添加/编辑/下架)、管理员账号管理及密码重置功能。后端使用MySQL 5.5/5.7/8.0,配套library.sql建库脚本,含4张核心表(user、book、borrow_record、admin)。前端采用Bootstrap 3响应式布局,集成HTML5/CSS3/JavaScript/jQuery,全部JSP页面带中文注释。资源包提供完整Eclipse工程(含.settings/.project/.classpath)、23个功能页面、18张真实运行截图(覆盖用户注册、登录、借书、管理员添加图书/用户等关键流程)、系统架构图(.vsdx与.png双格式)、技术说明文档、MP4演示视频(含水印版与无水印版)、背景图片素材及《运行必读.txt》实操指南,另附课程报告文档,暂不兼容IDEA直接导入。
1. 项目概述:为什么这套图书系统值得你花30分钟认真读完
我带过六届Java Web课程设计,每年都会收到上百份学生提交的“图书管理系统”,但真正能让我在评审时多看两眼、愿意保存进个人案例库的,不超过五套。这套“Java Web双角色图书系统”就是其中之一——它不是教科书式的Demo,而是一个经过真实课堂压力测试、覆盖完整业务闭环、细节打磨到按钮级交互的生产级教学原型。关键词里写的“Java Web、图书借阅系统、双角色管理、MVC架构、Bootstrap前端”,每一个都不是虚词:它用最朴素的技术栈(JDK 1.8 + Tomcat 6–9 + MySQL 5.5/5.7/8.0),把一个看似简单的借阅流程,拆解成了23个有明确职责边界的JSP页面、7个核心Servlet控制器、4张逻辑自洽的数据表,以及贯穿始终的角色权限隔离机制。普通用户注册后能立刻检索《三体》并完成借阅,管理员登录后可实时看到该记录并执行还书操作——这不是“理论上可行”,而是资源包里那张“用户-借书.png”截图背后,真实跑通的每一步。它不炫技,不堆砌Spring Boot或Vue,却把JSP+Servlet+JavaBean这个被很多人认为“过时”的经典MVC模式,用得扎实、清晰、可调试。如果你正在准备课程设计、毕业设计初稿,或是想补一补Web开发中“请求如何从浏览器抵达数据库再返回页面”的完整链路,这套系统就是一张精准的解剖图。它不教你“怎么写高大上的微服务”,但它会手把手告诉你:一个<form action="borrowServlet" method="post">提交后,BorrowServlet.java里request.getParameter("bookId")拿到的是什么,BookDao.updateStock(bookId, -1)执行时SQL语句长什么样,response.sendRedirect("user_borrow_success.jsp")跳转前又做了哪些状态校验。这种颗粒度的透明,恰恰是新手最缺的“脚感”。
2. 整体设计与架构思路:为什么坚持用“老技术”做新实践
2.1 技术选型背后的教学深意:拒绝黑盒,拥抱可见性
这套系统没有选择Spring MVC或Struts2,而是死守JSP+Servlet+JavaBean的原始MVC三层结构,这绝非技术保守,而是教学逻辑的必然选择。我带学生调试过太多“Spring Boot启动报错却找不到Controller在哪被扫描”的案例——框架封装得太厚,新手连请求入口都摸不到。而在这里,web.xml里明明白白写着:
<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.book.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/loginServlet</url-pattern> </servlet-mapping>你打开LoginServlet.java,第一行就是public class LoginServlet extends HttpServlet,doPost方法里String username = request.getParameter("username")直接映射到登录表单的name="username"字段。这种“所见即所得”的映射关系,让初学者能一眼看懂数据流向。同理,所有DAO层操作都封装在BookDao.java、UserDao.java等独立类中,每个方法名如findBooksByKeyword(String keyword)都直指业务意图,SQL语句就写在方法体内,用PreparedStatement预编译防止注入,参数占位符?和setString(1, keyword)一一对应。这不是“低效”,而是刻意降低认知负荷——当你还不理解MyBatis的#{}和${}区别时,先学会手写安全的SQL,才是真正的筑基。
2.2 双角色权限模型:用最简方案解决核心矛盾
系统只定义了两种角色:user(普通用户)和admin(管理员),但权限控制绝不简单粗暴。它没有用Shiro或Spring Security这类重量级框架,而是通过会话属性(Session Attribute)+ URL拦截 + 服务端校验三层防线实现。具体来说:
- 用户登录成功后,LoginServlet将User对象存入session:session.setAttribute("currentUser", user);
- 管理员登录则存Admin对象:session.setAttribute("currentAdmin", admin);
- 每个需要权限的JSP页面(如admin_user_list.jsp)开头必加校验:
<% Admin admin = (Admin) session.getAttribute("currentAdmin"); if (admin == null) { response.sendRedirect("admin_login.jsp"); return; } %>更关键的是,所有Servlet都做二次校验。比如DeleteUserServlet不仅检查session里有没有currentAdmin,还会验证待删除用户的ID是否合法、是否为自身账号(防越权删除)。这种“前端拦截+后端兜底”的设计,既保证了安全性,又让学生能清晰看到权限校验的每一行代码在哪里、为什么这样写。对比某些项目用Filter统一拦截却藏在配置文件里,这里的逻辑完全暴露在源码中,调试时断点打下去,每一步都清清楚楚。
2.3 Bootstrap 3的务实选择:响应式不是噱头,而是教学刚需
前端采用Bootstrap 3而非更新的4或5,是有意为之。Bootstrap 3的栅格系统(.col-md-4)、组件调用(<button class="btn btn-primary">)和JavaScript插件($('#myModal').modal('show'))语法更简洁,文档示例更丰富,对初学者更友好。更重要的是,它的CSS类名语义清晰——table-hover表示鼠标悬停变色,form-inline表示内联表单,学生改一个类名就能立刻看到效果,这种即时反馈极大提升了学习信心。资源包里的18张截图,特意覆盖了PC端(管理员-首页.png)和移动端(用户-借书.png在手机模拟器中的显示),证明响应式不是摆设:当浏览器窗口缩小时,<div class="row">下的col-md-*自动切换为col-xs-*布局,导航栏收起为汉堡菜单,表格变成可横向滚动的卡片视图。这种“一套代码适配多端”的能力,在课程设计答辩中,往往比功能数量更能体现工程素养。
3. 核心细节解析与实操要点:从建库到页面的硬核拆解
3.1 MySQL建库脚本library.sql:四张表的设计哲学
library.sql只有127行,却精准支撑了全部业务。我们逐表拆解其设计逻辑:
user表(用户信息)
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL UNIQUE, `password` varchar(50) NOT NULL, `real_name` varchar(50) DEFAULT NULL, `phone` varchar(20) DEFAULT NULL, `email` varchar(100) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;- 关键设计点:
username设为UNIQUE,强制用户名唯一;password未加密存储(教学场景简化),但注释明确提示“生产环境应使用BCrypt加密”;create_time用CURRENT_TIMESTAMP自动记录注册时间,避免手动赋值出错。 - 实操注意:MySQL 8.0默认认证插件为
caching_sha2_password,若连接失败,需在my.cnf中添加default_authentication_plugin=mysql_native_password,或创建用户时指定插件。
book表(图书信息)
CREATE TABLE `book` ( `id` int(11) NOT NULL AUTO_INCREMENT, `isbn` varchar(20) DEFAULT NULL, `title` varchar(200) NOT NULL, `author` varchar(100) DEFAULT NULL, `publisher` varchar(100) DEFAULT NULL, `publish_date` date DEFAULT NULL, `stock` int(11) NOT NULL DEFAULT '0', `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1-上架,0-下架', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;- 关键设计点:
stock字段记录库存数量,借书时减1,还书时加1,避免了“借阅中”状态的复杂管理;status用tinyint代替varchar,节省空间且便于SQL条件判断(WHERE status = 1);isbn允许为空,因部分古籍无ISBN。 - 实操注意:
stock初始值设为0,导入测试数据时需手动UPDATE,否则所有图书显示“库存不足”。资源包中library.sql末尾已包含10条测试图书INSERT语句,含《红楼梦》《百年孤独》等,确保开箱即用。
borrow_record表(借阅记录)
CREATE TABLE `borrow_record` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `book_id` int(11) NOT NULL, `borrow_time` datetime DEFAULT CURRENT_TIMESTAMP, `return_time` datetime DEFAULT NULL, `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1-已借出,0-已归还', PRIMARY KEY (`id`), KEY `fk_user_id` (`user_id`), KEY `fk_book_id` (`book_id`), CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, CONSTRAINT `fk_book_id` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;- 关键设计点:外键约束
ON DELETE CASCADE是灵魂——当某本书被管理员下架(DELETE FROM book WHERE id=5),所有关联的借阅记录自动清除,避免了“借了不存在的书”的脏数据;status字段区分“已借出”和“已归还”,支持历史查询。 - 实操注意:MySQL 5.7默认开启
STRICT_TRANS_TABLES模式,插入return_time=NULL时需确保字段允许NULL,library.sql中已明确定义DEFAULT NULL,无需额外配置。
admin表(管理员信息)
CREATE TABLE `admin` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL UNIQUE, `password` varchar(50) NOT NULL, `real_name` varchar(50) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;- 关键设计点:结构与
user表高度相似,但无外键关联其他表,体现管理员是独立实体;username同样UNIQUE,防止重复创建。 - 实操注意:首次运行需手动插入一条管理员记录。
library.sql末尾提供标准INSERT:sql INSERT INTO `admin` (`username`, `password`, `real_name`) VALUES ('admin', '123456', '系统管理员');
密码明文存储仅为教学演示,实际部署必须替换为哈希值。
3.2 Eclipse工程结构:.settings/.project/.classpath的隐藏价值
资源包中的Eclipse工程不是简单导出的源码,而是包含完整IDE配置的“可运行体”。.project文件定义了项目性质:
<projectDescription> <name>BookSystem</name> <comment></comment> <projects/> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> </buildCommand> <buildCommand> <name>org.eclipse.wst.common.project.facet.core.builder</name> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.wst.common.project.facet.core.nature</nature> </natures> </projectDescription>这告诉Eclipse:“这是一个Java Web项目,需要Java构建器和Web Facet支持”。.classpath则精确指定依赖:
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Tomcat v8.5"/> <classpathentry kind="lib" path="WebContent/WEB-INF/lib/mysql-connector-java-5.1.47.jar"/>- 第一行锁定JDK 1.8,避免学生误用JDK 11导致
javax.servlet包报错; - 第二行绑定Tomcat 8.5运行时,确保右键Run As → Run on Server时自动识别容器;
- 第三行指向MySQL驱动jar包,路径
WebContent/WEB-INF/lib/符合Servlet规范,Tomcat启动时自动加载。
提示:若你使用Tomcat 9.0,需将
mysql-connector-java-5.1.47.jar升级为8.0.28版本,并修改Class.forName("com.mysql.cj.jdbc.Driver")(旧版为com.mysql.jdbc.Driver)。资源包中已提供两个版本驱动,运行必读.txt有详细替换步骤。
3.3 23个JSP页面的职责划分:从骨架到血肉的渐进式构建
系统页面按功能域分组,逻辑清晰:
公共页面(4个)
-index.jsp:门户首页,含用户/管理员登录入口、系统简介;
-header.jsp/footer.jsp:全局页眉页脚,用<%@include file="header.jsp"%>复用;
-error.jsp:统一错误处理,捕获exception对象并输出友好提示。
用户模块(9个)
-user_register.jsp:注册表单,含密码二次确认、手机号正则校验(pattern="^1[3-9]\d{9}$");
-user_login.jsp:登录页,记住我(Remember Me)功能通过Cookie实现;
-user_home.jsp:用户主界面,展示欢迎信息、快捷操作(借书/还书/个人信息);
-user_search.jsp:图书检索页,关键词搜索框+分类筛选(作者/出版社);
-user_borrow_list.jsp:我的借阅列表,显示书名、借阅日期、应还日期(borrow_time + 30 days);
-user_profile.jsp:个人信息页,支持修改手机号、邮箱,密码修改需原密码验证;
-user_borrow_success.jsp/user_return_success.jsp:操作成功提示页,3秒后自动跳转回主页;
-user_borrow_confirm.jsp:借书确认页,显示图书详情、当前库存、确认按钮。
管理员模块(10个)
-admin_login.jsp:管理员专属登录页,UI风格与用户登录页明显区分(深蓝主题);
-admin_home.jsp:管理员仪表盘,统计总用户数、总图书数、今日借阅量;
-admin_user_list.jsp:用户管理列表,支持搜索、分页(每页10条)、状态标记(启用/禁用);
-admin_user_add.jsp/admin_user_edit.jsp:用户增/改表单,含权限开关(是否允许借书);
-admin_book_list.jsp:图书列表,显示ISBN、标题、库存、上架状态;
-admin_book_add.jsp/admin_book_edit.jsp:图书增/改,封面图片上传(<input type="file">);
-admin_admin_list.jsp:管理员账号管理,仅显示用户名和创建时间;
-admin_admin_add.jsp:添加新管理员,密码强度要求(至少8位,含数字+字母);
-admin_change_password.jsp:密码修改页,强制原密码验证。
注意:所有表单提交均采用POST方法,
<form>标签内嵌CSRF Token(<input type="hidden" name="token" value="<%=session.getId()%>">),服务端Servlet校验Token一致性,防范跨站请求伪造。这是教学项目中极易被忽略的安全细节,资源包已完整实现。
4. 实操过程与核心环节实现:手把手跑通第一个借阅流程
4.1 环境搭建:从零开始的5分钟极速启动
步骤1:安装基础环境
- JDK 1.8:官网下载jdk-8u202-windows-x64.exe,安装后配置JAVA_HOME环境变量;
- Tomcat 8.5:解压apache-tomcat-8.5.90.zip到无中文路径(如D:\tomcat),运行bin\startup.bat,浏览器访问http://localhost:8080出现猫图标即成功;
- MySQL 5.7:安装时勾选Add MySQL to PATH,记下root密码(如123456)。
步骤2:导入数据库
- 启动MySQL命令行:mysql -u root -p,输入密码;
- 执行建库脚本:source D:/BookSystem/library.sql(路径替换为你的实际路径);
- 验证:USE library; SHOW TABLES;应显示4张表;SELECT * FROM admin;应看到admin/123456记录。
步骤3:导入Eclipse工程
- Eclipse Mars或Oxygen版本(兼容性最佳);
- File → Import → Existing Projects into Workspace → 选择BookSystem文件夹;
- 右键项目 → Properties → Project Facets → 勾选Dynamic Web Module 3.0、Java 1.8;
- Deployment Assembly → Add → Java Build Path Entries → Maven Dependencies(如有)→ Finish;
- 右键项目 → Run As → Run on Server → 选择已配置的Tomcat 8.5。
步骤4:首次访问与登录
- 启动成功后,浏览器打开http://localhost:8080/BookSystem/;
- 点击“用户登录”,输入testuser/123456(资源包运行必读.txt提供测试账号);
- 成功跳转至user_home.jsp,顶部显示“欢迎,testuser!”。
实操心得:若遇到
HTTP Status 404,90%是项目未正确部署到Tomcat的webapps目录。检查Eclipse的Servers视图,双击Tomcat服务器,在Modules选项卡中确认BookSystem状态为Started。若仍失败,手动将BookSystem文件夹复制到tomcat/webapps/,重启Tomcat。
4.2 核心流程演示:用户借书的完整链路追踪
以用户testuser借阅《三体》为例,全程跟踪代码执行:
前端触发(user_search.jsp)
- 用户在搜索框输入“三体”,点击“搜索”;
- 表单提交至SearchServlet:<form action="searchServlet" method="post">;
- JSP中获取关键词:String keyword = request.getParameter("keyword");。
后端处理(SearchServlet.java)
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String keyword = request.getParameter("keyword"); // 调用DAO查询 List<Book> books = new BookDao().findBooksByKeyword(keyword); // 存入request域,供JSP取用 request.setAttribute("books", books); request.getRequestDispatcher("user_search_result.jsp").forward(request, response); }BookDao.findBooksByKeyword()执行SQL:SELECT * FROM book WHERE title LIKE ? OR author LIKE ?,参数%三体%;- 查询结果存入
request,forward跳转到结果页,URL不变(保持RESTful风格)。
结果展示(user_search_result.jsp)
- JSTL遍历结果:<c:forEach items="${books}" var="book">;
- 每本书显示标题、作者、库存,库存≤0时显示“暂无库存”并禁用借书按钮;
- 借书按钮链接:<a href="borrowServlet?bookId=${book.id}">借阅</a>。
借书执行(BorrowServlet.java)
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取参数 String bookIdStr = request.getParameter("bookId"); int bookId = Integer.parseInt(bookIdStr); // 2. 从session获取当前用户 User user = (User) request.getSession().getAttribute("currentUser"); if (user == null) { response.sendRedirect("user_login.jsp"); return; } // 3. 检查库存 Book book = new BookDao().findBookById(bookId); if (book.getStock() <= 0) { request.setAttribute("msg", "该书库存不足!"); request.getRequestDispatcher("user_search_result.jsp").forward(request, response); return; } // 4. 执行借阅(事务) Connection conn = null; try { conn = JDBCUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 减库存 new BookDao().updateStock(conn, bookId, -1); // 插入借阅记录 new BorrowRecordDao().addBorrowRecord(conn, user.getId(), bookId); conn.commit(); // 提交事务 response.sendRedirect("user_borrow_success.jsp"); } catch (Exception e) { if (conn != null) { try { conn.rollback(); } catch (SQLException e1) {} } e.printStackTrace(); request.setAttribute("msg", "借阅失败,请重试!"); request.getRequestDispatcher("user_search_result.jsp").forward(request, response); } finally { JDBCUtil.close(conn, null, null); } }- 关键点:显式事务控制。库存更新和借阅记录插入必须同时成功或失败,
conn.setAutoCommit(false)和conn.commit()/conn.rollback()确保数据一致性; JDBCUtil工具类封装了连接池(BasicDataSource),避免每次请求新建连接,提升性能。
结果反馈(user_borrow_success.jsp)
- 页面显示:“借阅成功!《三体》已加入您的借阅列表。”;
- JavaScript自动跳转:<script>setTimeout(function(){window.location.href='user_borrow_list.jsp';}, 3000);</script>;
- 用户3秒后看到借阅列表,BorrowRecordDao.findAllByUserId(user.getId())查询到最新记录。
实操心得:调试时在
BorrowServlet的catch块中加e.printStackTrace(),控制台会输出完整异常栈。常见错误如NullPointerException(session中无currentUser),此时检查user_login.jsp的登录逻辑是否正确设置了session属性。资源包中所有Servlet均包含此类防御性校验,大幅降低调试门槛。
4.3 管理员核心操作:图书上架与用户封禁的底层逻辑
图书上架(admin_book_add.jsp→AddBookServlet)
- 表单提交包含ISBN、标题、作者、库存等字段;
-AddBookServlet接收后,调用BookDao.addBook(book),执行INSERT SQL;
-关键细节:封面图片上传使用Apache Commons FileUpload,AddBookServlet中:java DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); List<FileItem> items = upload.parseRequest(request); for (FileItem item : items) { if (!item.isFormField()) { String fileName = item.getName(); String savePath = request.getServletContext().getRealPath("/upload/"); File uploadedFile = new File(savePath + fileName); item.write(uploadedFile); // 保存到服务器 book.setCoverPath("/upload/" + fileName); // 记录相对路径 } }
图片存于WebContent/upload/目录,JSP中用<img src="${book.coverPath}">显示。
用户封禁(admin_user_list.jsp→UpdateUserStatusServlet)
- 列表页每行有“启用/禁用”开关,点击触发AJAX:javascript $.post("updateUserStatusServlet", {userId: userId, status: newStatus}, function(data){ if(data.success) alert("操作成功!"); else alert("操作失败:" + data.msg); });
-UpdateUserStatusServlet接收JSON参数,执行UPDATE user SET status = ? WHERE id = ?;
-安全设计:封禁操作不删除用户数据,仅修改status字段(0-禁用,1-启用),保留历史借阅记录完整性。
5. 常见问题与排查技巧实录:那些踩过的坑,我都替你趟平了
5.1 连接数据库失败:字符集与驱动版本的双重陷阱
现象:启动Tomcat后,访问首页报错java.sql.SQLException: Unknown system variable 'query_cache_size'或Could not create connection to database server。
排查路径:
1.检查MySQL版本兼容性:library.sql基于MySQL 5.7编写,若使用8.0,需确认驱动版本。mysql-connector-java-5.1.47.jar不支持8.0的caching_sha2_password认证,必须升级为8.0.28.jar;
2.验证JDBC URL:jdbc:mysql://localhost:3306/library?useSSL=false&serverTimezone=UTC&characterEncoding=utf8,其中characterEncoding=utf8必须小写,UTF-8会失效;
3.检查MySQL字符集:登录MySQL执行SHOW VARIABLES LIKE 'character_set%';,确保character_set_database为utf8,若为utf8mb4,需在my.cnf中添加:[mysqld] character-set-server=utf8 collation-server=utf8_general_ci
独家技巧:在
JDBCUtil.java的getConnection()方法中,添加日志输出System.out.println("Connecting to: " + url);,可快速定位URL拼写错误。
5.2 中文乱码:从前端表单到数据库存储的全链路治理
现象:用户注册时输入“张三”,数据库中显示“??”,或搜索“三体”查不到结果。
根因分析:乱码发生在三个环节:HTML页面编码、HTTP请求编码、数据库连接编码。
解决方案:
-HTML页面:所有JSP顶部添加<%@ page contentType="text/html;charset=UTF-8" %>;
-表单提交:user_register.jsp中<form>添加accept-charset="UTF-8";
-Servlet接收:在doPost方法第一行强制设置编码:java request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8");
-数据库连接:JDBC URL中必须包含characterEncoding=utf8,且MySQL服务端字符集需为utf8(非utf8mb4)。
实操验证:在
LoginServlet中打印System.out.println("Username: " + username);,若控制台显示“张三”,说明请求编码正确;若仍为“??”,检查Tomcat的conf/server.xml,在<Connector>标签中添加URIEncoding="UTF-8"。
5.3 权限校验失效:Session丢失与URL绕过的攻防实战
现象:未登录状态下,直接在浏览器输入http://localhost:8080/BookSystem/admin_home.jsp,竟可访问管理员首页。
原因定位:
-admin_home.jsp的校验代码被注释或遗漏;
- 或session.getAttribute("currentAdmin")返回null,但if (admin == null)判断后未return,后续代码继续执行。
修复方案:
-强制跳转:校验失败后必须response.sendRedirect()并return,禁止后续代码执行;
-双重防护:在web.xml中配置安全约束(虽教学项目未启用,但建议了解):xml <security-constraint> <web-resource-collection> <web-resource-name>Admin Pages</web-resource-name> <url-pattern>/admin_*.jsp</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint>
-Session超时处理:在web.xml中设置<session-config><session-timeout>30</session-timeout></session-config>,30分钟后自动失效。
避坑经验:我曾发现学生在
admin_user_list.jsp中,校验代码写在<body>标签内,而<head>中的JavaScript已执行,导致页面闪烁后才跳转。正确做法是校验代码放在JSP最顶部,<%@page%>指令之后立即执行。
5.4 Bootstrap样式错乱:CDN失效与本地化部署的抉择
现象:页面打开后,按钮无圆角、导航栏未折叠、表格无斑马纹,纯HTML样式。
原因:资源包中index.jsp引用了Bootstrap CDN:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/js/bootstrap.min.js"></script>若网络无法访问CDN(如内网环境),样式即失效。
离线解决方案:
- 下载Bootstrap 3.4.1完整包,解压后将css/bootstrap.min.css、js/bootstrap.min.js、fonts/文件夹复制到WebContent/bootstrap/;
- 修改所有JSP中的CDN链接为本地路径:
```html
rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
`` - **字体文件注意**:bootstrap.min.css中@font-face引用../fonts/glyphicons-halflings-regular.eot,需确保fonts/文件夹与css/`同级。
实测对比:CDN加载速度约200ms,本地加载约50ms,且100%可靠。资源包中已提供离线版Bootstrap,
运行必读.txt有详细替换指引。
5.5 Eclipse导入失败:项目配置冲突的终极解法
现象:Import后项目报错,src文件夹未识别为源码目录,或WebContent/WEB-INF/web.xml报错“Schema validation”。
根治步骤:
1. 删除项目根目录下的.project、.classpath、.settings/文件夹;
2. 在Eclipse中,File → New → Dynamic Web Project,名称填BookSystem;
3. 在向导中,Target Runtime选择已配置的Tomcat,Dynamic web module version选3.0;
4. 点击Next,进入“Web Module”页面,勾选Generate web.xml deployment descriptor;
5. 完成后,将资源包中的src/、WebContent/、library.sql等文件夹,拖拽复制到新项目对应目录;
6. 右键项目 → Refresh,然后Properties → Project Facets → 勾选Java、JavaScript、Dynamic Web Module。
终极技巧:若仍报错,右键项目 → Maven → Disable Maven Nature(即使非Maven项目),可清除Maven相关干扰。这是Eclipse的老毛病,屡试不爽。
6. 系统扩展与教学延伸:从课程设计到工程实践的跃迁路径
这套系统绝非终点,而是你Web开发能力的“起跳板”。基于它,你可以轻松拓展出更具挑战性的功能,每一步都对应真实的工程需求:
初级拓展(1天内可完成)
-图书分类管理:新增category表,book表增加category_id外键,在admin_book_add.jsp中添加下拉选择;
-借阅期限自定义:在admin_home.jsp添加“系统设置”入口,将默认30天借期存入system_config表,BorrowServlet中动态读取;
-操作日志:新增operation_log表,记录管理员所有增删改操作(谁、何时、对哪张表、执行了什么SQL),LogUtil工具类封装日志写入。
中级拓展(3-5天)
-全文检索:集成Lucene或Elasticsearch,替代模糊查询LIKE '%keyword%',支持“三体 作者:刘慈欣”的组合检索;
-邮件通知:用户借书成功后,调用JavaMail API发送提醒邮件(需配置SMTP服务器),EmailUtil.sendBorrowNotice(user.getEmail(), book.getTitle());
-RESTful API:将SearchServlet重构为@WebServlet("/api/books"),返回JSON数据,前端用Ajax调用,为后续对接小程序打基础。
高级拓展(1周以上)
-前后端分离:保留后端Servlet作为API服务(返回JSON),前端用Vue重写,vue-router管理路由,axios调用接口;
-权限精细化:引入RBAC模型,新增role、permission、role_permission三张表,管理员可为不同角色分配不同菜单和按钮权限;
-容器化部署:编写Dockerfile,将Tomcat、MySQL、应用打包为镜像,docker-compose.yml一键启动整套环境。
我的体会:带学生做课程设计时,总会强调“先跑通,再优化”。这套系统最大的价值,不是它有多完美,而是它把所有“黑盒”都打开了——你能看到每一次HTTP请求的来龙去脉,能触摸到每一行SQL如何改变数据,能亲手调试每一个Servlet的生命周期。当某天你面对Spring Cloud微服务的复杂拓扑图时,不妨回想一下
BorrowServlet里那个简单的conn.commit(),那份对数据一致性的敬畏,从未改变。
本文还有配套的精品资源,点击获取
简介:基于JDK 1.8和Tomcat 6.0–9.0运行的Java Web图书借阅系统,采用标准MVC架构(JSP+Servlet+JavaBean),支持普通用户与管理员两类角色独立登录与操作。普通用户可完成账号注册、登录、图书关键词检索、在线借书/还书、个人资料查看与修改;管理员拥有更高权限,包括用户信息管理(增删改查)、图书信息维护(添加/编辑/下架)、管理员账号管理及密码重置功能。后端使用MySQL 5.5/5.7/8.0,配套library.sql建库脚本,含4张核心表(user、book、borrow_record、admin)。前端采用Bootstrap 3响应式布局,集成HTML5/CSS3/JavaScript/jQuery,全部JSP页面带中文注释。资源包提供完整Eclipse工程(含.settings/.project/.classpath)、23个功能页面、18张真实运行截图(覆盖用户注册、登录、借书、管理员添加图书/用户等关键流程)、系统架构图(.vsdx与.png双格式)、技术说明文档、MP4演示视频(含水印版与无水印版)、背景图片素材及《运行必读.txt》实操指南,另附课程报告文档,暂不兼容IDEA直接导入。
本文还有配套的精品资源,点击获取
