ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSP+Servlet+JDBC后台管理系统源码实战:登录分页过滤部署全解析

JSP+Servlet+JDBC后台管理系统源码实战:登录分页过滤部署全解析 简介一套基于 JSPServletJSJDBCMySQL 原生技术栈开发的后台管理系统源码面向 Java Web 初学者、毕业设计或课程设计场景解决原生 Servlet 项目从登录鉴权到数据管理的完整搭建问题。系统实现了登录、注册、产品管理、分页、图片上传、退出和过滤器拦截等功能产品管理模块支持增删改查、详情查看与批量删除未登录用户直接访问主界面会被过滤器拦截回登录页逻辑清晰适合学习 Servlet 生命周期、Session 控制与 JDBC 分层开发。压缩包共 92 个文件以 java 源文件、jsp 页面、class 编译文件、xml 配置、jar 依赖库和 sql 数据库脚本等类型为主压缩后仅 3.04MB内附工程导入说明、运行必读文档、数据库建表脚本及 MyEclipse/Eclipse 工程配置便于快速导入 JDK1.7Tomcat7MySQL5.x 环境部署。已有 3371 人学习下载适合需要参考原生 Servlet 分页查询、文件上传实现以及想要快速梳理后台管理系统代码结构的开发者阅读并二次扩展。1. 一套 jspservletjdbc 后台管理系统源码拿得到、跑得起、讲得清如果你要的是课程设计、毕设或者公司内部快速搭一个后台管理系统JSPServletJDBCMySQL 这套原生技术栈往往比 Spring Boot 全家桶更省事——因为每个环节都能讲清楚。说个实际经历我帮朋友排查毕设老师随口问“Servlet 生命周期怎么回事”用框架的学生直接愣住了。今天拆的这份 servletjdbc 后台管理系统源码把登录、注册、产品管理、分页、图片上传、过滤器权限拦截都做齐了数据库脚本、部署说明、运行必读也一应俱全。你要一个不依赖框架、每行逻辑都能在源码里找到对应位置的管理系统它就是一个。2. 工程导入与数据库初始化从 mydb.sql 建库到 Tomcat 部署的完整路径很多第一次拿到这份源码的人卡住的不是代码本身而是打开压缩包那一刻——一堆不知道干什么用的文件铺在眼前。这一章我带你按顺序把文件盘一遍再走两遍关键流程一次是数据库一次是工程导入。2.1 压缩包文件清单哪些文件必须动哪些只是 IDE 留下的痕迹xianfengProject 压缩包解压之后能看到的目录和文件我按用途给你分类文件/目录用途要不要手工改xianfengProject 主目录主工程里面还有 src 和 WebRoot 两个子目录导入时选它不用改mydb.sql建库脚本包含表结构和初始数据需要手动执行一次工程导入步骤.docxEclipse/MyEclipse 导入图文流程按它操作即可技术实现说明.docx登录、注册、产品管理、分页、图片上传的实现说明写文档或答辩时参考运行必读.txt环境要求、启动顺序、默认账号密码第一次启动前必看.project / .classpath / .mymetadata / .settings / .idea / .myeclipseIDE 工程配置文件正常情况下不要碰out编译输出目录不用管这里有个很容易踩的认知误区很多人觉得 .idea、.settings 这类文件是垃圾一键删掉再导入结果 Eclipse 识别不了工程连 Web 项目图标都没了。实际上 .project 里声明了工程类型.mymetadata 里是 MyEclipse 的 Web 配置删掉任何一个IDE 都可能不再认为这是 Java Web 工程后面 Tomcat 就无法把它发布成 Web 应用。我的习惯是原样导入隐藏文件一个都不动省得给自己挖坑。2.2 mydb.sql 建库脚本执行前先确认这三件事拿到别人的 SQL 脚本我从来不会双击直接导入而是先打开看一遍。这份 mydb.sql 里主要建的是用户表和产品表外加初始账号数据真正执行之前建议核对三件事第一数据库版本。原项目要求 MySQL 5.5、5.6 或 5.7如果你用的是 MySQL 8.x建表语句大概率能跑但 JDBC 驱动和连接串写法可能要调整避坑章节我细说。第二字符集。建库语句通常带 DEFAULT CHARACTER SET如果导入时被 Navicat 等的默认选项覆盖中文可能出现乱码。第三自增主键。用户表主键通常是 int 自增如果脚本执行后自增起始值不对分页和新增记录的表现都会跟着出问题。-- mydb.sql 中典型的建库建表语句示意 CREATE DATABASE IF NOT EXISTS mydb DEFAULT CHARACTER SET utf8; USE mydb; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, realname VARCHAR(50), create_date DATETIME ); CREATE TABLE t_product ( id INT PRIMARY KEY AUTO_INCREMENT, pname VARCHAR(100) NOT NULL, price DECIMAL(10,2), description VARCHAR(500), image VARCHAR(255), create_date DATETIME ); INSERT INTO t_user (username, password, realname) VALUES (admin, 123456, 管理员);这段脚本里t_user 表我特意给 username 加了 UNIQUE 约束产品表保留了 image 字段存放图片文件名。执行时用命令行或者 Navicat 都行。命令行常见做法是登录 MySQL 后直接用 source 执行mysql -uroot -p # 输入密码后执行下面两行 source /你的绝对路径/mydb.sql show tables;source 后面的路径最好用绝对路径相对路径写错时 MySQL 报错找不到文件很多人会误以为脚本本身坏了。用 Navicat 的话直接在连接上右键“运行 SQL 文件”选到 mydb.sql跑完看表列表里有没有 t_user 和 t_product 这两张表再双击 t_user 确认 admin 初始账号存在。2.3 Eclipse/MyEclipse 导入工程识别成 Web 项目才是关键工程导入是整个部署链路里翻车率最高的环节。源码包默认支持 Eclipse 或 MyEclipse配套 JDK 1.7 和 Tomcat 7。用 MyEclipse 的话不用装额外插件用纯 Eclipse 需要先装 WTP不然导入后没有 Run on Server 选项。操作流程分四步第一步Eclipse 里选择 File → Import → General → Existing Projects into Workspace。第二步点 Select root directory定位到能看到 .project 文件的那一层也就是 xianfengProject 主目录。如果下面没显示工程多半是选到了内层目录或者目录层级不对。第三步确认项目出现后把 Copy projects into workspace 勾不勾都行然后 Finish。这时 Eclipse 会开始构建等右下角进度条消失。第四步配置 TomcatWindows → Preferences → Server → Runtime Environments → Add选 Apache Tomcat v7.0指定安装目录JRE 选 1.7。工程导入后项目名上如果有一个地球小图标说明被识别成了 Web 工程如果还是普通文件夹图标说明 .project 里的配置没生效检查一下是不是把 WebRoot 当根目录导入了。最后右键 xianfengProject → Run As → Run on Server选 Tomcat 7 启动控制台出现 Server startup in xxx ms 就说明这一条链路通了浏览器访问 http://localhost:8080/xianfengProject/ 应该能看到 index.jsp 登录页。3. 登录注册与过滤器Servlet 权限控制的完整链路后台管理系统的第一道门就是登录。这一章我把登录、注册、过滤器三个功能拆开它们串在一起就是完整的权限控制链路也是答辩和面试里最容易追问的部分。3.1 登录流程Session 的创建、使用与跳转index.jsp 是默认主页页面上提供登录和注册入口。登录表单提交后请求交给 LoginServlet 的 doPost 方法核心流程是四步设置请求编码、接收用户名密码、调 UserDao 查库、根据结果决定跳转还是转发。protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); UserDao dao new UserDao(); User user dao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(currentUser, user); response.sendRedirect(request.getContextPath() /productList.jsp); } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(index.jsp).forward(request, response); } }这段代码里有个值得注意的细节登录失败时用的是 forward 转发转发是服务端跳转地址栏还是 loginServlet刷新页面会再次提交表单如果想地址栏也回到 index.jsp可以改成 sendRedirect。两种方式在面试中被问到“forward 和 redirect 区别”时拿这个场景举例最直观。另外findByUsernameAndPassword 方法里如果用的是字符串拼接 SQL建议改成 PreparedStatement 预编译既防 SQL 注入也是老师最爱问的安全考点。3.2 注册功能用户名重复检查和 PreparedStatement 插入注册的逻辑不复杂用户填用户名、密码、确认密码提交到注册 Servlet先校验两次密码是否一致再确认用户名是否已存在都不冲突才插入新记录然后跳回登录页。很多初学者写注册容易漏掉用户名重复检查直接 insert。于是同一个用户名注册出多条记录登录时 findByUsernameAndPassword 查到两个结果有的 DAO 实现是 list.get(0) 取第一条有的直接抛异常。我一般会做两层保险业务层先做 count 判断数据库层给 username 加 UNIQUE 约束。// 注册逻辑中的关键代码先查重复再插入 public boolean register(User user) { if (isUsernameExists(user.getUsername())) { return false; // 用户名已存在注册失败 } String sql INSERT INTO t_user(username, password, realname) VALUES(?,?,?); // 使用 PreparedStatement 执行插入第一个参数是占位符 try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, user.getUsername()); ps.setString(2, user.getPassword()); ps.setString(3, user.getRealname()); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } }这里 password 字段在源码里大概率是明文存取。你要拿去交作业或者上线最好在注册和登录两边都做一层 MD5 或 SHA-256 摘要后再比对不然数据库被拖库的话账号密码就全裸奔了。加密逻辑不复杂但加了之后整个项目的安全观感会好很多。3.3 过滤器未登录用户访问受保护页面一律拦回 index.jsp过滤器是这个项目里权限设计的关键。虽然 productList.jsp 可以被直接访问但过滤器会在请求到达 JSP 之前先拦一道检查 Session 里有没有 currentUser没有就重定向回 index.jsp。public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); // 放行登录、注册、静态资源和主页 if (uri.endsWith(index.jsp) || uri.endsWith(login) || uri.endsWith(register) || uri.endsWith(.js) || uri.endsWith(.css) || uri.endsWith(.jpg) || uri.endsWith(.png)) { chain.doFilter(request, response); return; } HttpSession session request.getSession(false); if (session null || session.getAttribute(currentUser) null) { response.sendRedirect(request.getContextPath() /index.jsp); return; } chain.doFilter(request, response); }这段代码有两个地方值得留意。第一request.getSession(false) 里的 false 非常关键意思是拿不到 Session 就返回 null而不是新建一个。如果写成 getSession()未登录用户在访问受保护页面时会被自动创建一个空 Session既浪费内存又会让拦截逻辑失效——因为下一次取 Session 时能拿到对象只是没有 currentUser 属性导致被放行到业务逻辑里。第二放行列表里必须包含 CSS、JS、图片这类静态资源以及登录注册接口本身。新手如果把过滤器路径写成 /* 又没放行静态资源就会出现“登录页面能显示但登录后页面样式全丢、图片全裂”的怪现象原因不是代码逻辑问题而是 CSS 和图片都被过滤器拦住了。真正的过滤范围应该限定在需要保护的路径上比如 /product/* 或 /admin/*这样既干净又不容易误伤。4. 产品管理实现分页、增删改查与图片上传的代码细节登录进来之后见到的就是产品管理页面。这一章是项目里业务量最重的地方我按分页、增删改查、图片上传三个模块拆开讲。4.1 分页设计pageNow、pageSize 与 totalPage 怎么联动分页是本项目的一个核心功能。页面上必须正确显示两件事当前在第几页、总共有多少条记录。首次进入默认显示所有产品的第一页翻页或搜索时通过 URL 参数把 pageNow 传给 ServletServlet 把总数、每页条数、当前页和当前页数据列表封装进一个 PageBean最后转发给 JSP 渲染。public PageBeanProduct findPage(int pageNow, int pageSize, String keyword) { PageBeanProduct pb new PageBean(); int totalCount dao.getTotalCount(keyword); int totalPage (totalCount pageSize - 1) / pageSize; int start (pageNow - 1) * pageSize; pb.setPageNow(pageNow); pb.setPageSize(pageSize); pb.setTotalCount(totalCount); pb.setTotalPage(totalPage); pb.setList(dao.findPageList(start, pageSize, keyword)); return pb; }totalPage 的计算有个常见写法(totalCount pageSize - 1) / pageSize这个公式避免了浮点数直接整数除法向上取整比 Math.ceil 更干净。分页最容易写错的点是 limit 的起始位置数据库的 limit 是从 0 开始计数的第一页的 start 是 0第 pageNow 页的 start 是 (pageNow - 1) * pageSize。如果你直接写成 pageNow * pageSize第一页会跳过后 pageSize 条记录用户看到的就是从第二页开始的数据。还有一种更隐蔽的问题分页和搜索条件绑定在一起时翻页后搜索关键字丢失页面上明明选了关键字第二页却是全量数据。解决方案是翻页的 URL 参数里把 keyword 一起带上后面避坑部分我也提到这个。4.2 增删改查与批量删除DAO 层方法怎么组织ProductDAO 里通常有 save、update、delete、findById、findByPage 这么几个方法再加一个 getTotalCount。单条删除和批量删除是两种写法批量删除一般接收一个 id 数组SQL 拼成 IN 条件。IN 子句的动态参数由于个数不固定用 PreparedStatement 时得循环设值。// 批量删除ids 是选中的产品id数组例如 [3,5,9] public void deleteBatch(String[] ids) { StringBuilder sql new StringBuilder(DELETE FROM t_product WHERE id IN (); for (String ignored : ids) { sql.append(?,); } sql.deleteCharAt(sql.length() - 1); sql.append()); try (PreparedStatement ps conn.prepareStatement(sql.toString())) { for (int i 0; i ids.length; i) { ps.setInt(i 1, Integer.parseInt(ids[i])); } ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } }这段代码里有几个细节sql.append(?,) 循环结束会多一个逗号deleteCharAt(sql.length() - 1) 是把末尾逗号去掉。另外IN 后面的左括号和 ? 之间别丢空格拼接出来的 SQL 少了空格会直接语法报错。页面上批量删除的实现一般用 name 相同的 checkBoxJS 收集选中的值提交到 Servlet 后再转成 String[]。删除动作完成后记得重新查询列表页否则界面上的旧数据还在。这个模块你可以重点看原工程里 checkbox 的 name 属性配置以及批量删除按钮是怎么把多个值串起来提交的。4.3 图片上传磁盘路径、数据库字段和页面回显的配合图片上传是这个项目里最折腾的功能之一。Servlet 3.0 开始支持 MultipartConfig 注解的方式但在 JDK 1.7 Tomcat 7 的环境下工程很常见是使用 commons-fileupload 组件来处理你在 lib 目录里找找有没有 commons-fileupload 和 commons-io 这两个 jar是重要的线索。上传的流程可以分成三步前端表单以 multipart/form-data 方式提交Servlet 接收文件流并写入磁盘数据库的表里存文件名或相对路径而不是整个文件。下面是一段典型的处理逻辑if (ServletFileUpload.isMultipartContent(request)) { DiskFileItemFactory factory new DiskFileItemFactory(); ServletFileUpload upload new ServletFileUpload(factory); ListFileItem items upload.parseRequest(request); for (FileItem item : items) { if (!item.isFormField()) { String fileName new File(item.getName()).getName(); String suffix fileName.substring(fileName.lastIndexOf(.)); String newName UUID.randomUUID().toString() suffix; String realPath getServletContext().getRealPath(/upload); File dir new File(realPath); if (!dir.exists()) { dir.mkdirs(); } item.write(new File(dir, newName)); request.setAttribute(image, newName); } } }这段代码把客户端上传的文件名换成了 UUID避免多用户上传同名文件互相覆盖。注意 getRealPath(/upload) 取到的是 Tomcat 发布目录下项目的实际路径所以图片写入了发布目录页面里用 ${pageContext.request.contextPath}/upload/${p.image} 就能回显。这里有一个隐患是你必须清楚的如果 Eclipse 部署项目时用了临时发布目录项目重新发布后上传的图片会被清空。我通常在正式使用时把图片存储路径单独配置出来或者用 Tomcat 的虚拟目录映射但这作为课程设计已经够用了。5. 部署与运行避坑5 条翻车记录与对应解法这一章是我跑这份源码和类似 JSPServlet 老项目时积累的 5 条避坑记录按“现象 → 原因 → 解决”的顺序写给你。5.1 导入 Eclipse 后没有 Run on Server 选项现象导入工程后右键项目菜单里找不到 Run As → Run on Server。 原因Eclipse 没装 WTP Web 开发组件或者工程没被识别成 Dynamic Web Project。 解决先通过 File → New → Other 看看有没有 Dynamic Web Project 模板没有的话到 Eclipse Marketplace 装 Eclipse Web Developer Tools装完重启再右键项目Run on Server 就会出现。工程图标如果还是普通文件夹样式检查导入时选择的目录是否定位到了 .project 所在的那一层。5.2 登录页显示正常但输入任何账号都提示错误现象账号密码明明对登录还是失败或者页面上出现乱码提示。 原因请求参数编码没固定数据库字符集和 JSP 页面不一致。常见组合是 JSP 用 UTF-8而 mydb.sql 建库没有指定 UTF-8导致中文账号密码比对不上。 解决doPost 方法第一行强制 request.setCharacterEncoding(UTF-8)JDBC 连接串里加 characterEncodingutf-8再检查建库脚本里的 DEFAULT CHARACTER SET utf8。三个地方全部对齐中文乱码和登录失败才能根治。5.3 分页翻到第二页数据错乱或 SQL 报错现象第一页正常点第二页报 SQL 语法错误或者出现重复数据。 原因limit 的两个参数写反了。limit 后面第一个参数是 offset 偏移量第二个是 rows 行数两者互换会导致数据错乱。 解决start (pageNow - 1) * pageSizeSQL 里写 limit start, pageSize。注意 pageNow 从 1 开始第一页就是 limit 0,10第二页 limit 10,10。另外如果分页和搜索共存翻页时把 keyword 参数一并传过去否则第二页会丢失搜索条件显示全量数据。5.4 上传的图片在页面上裂图现象上传成功数据库里也有文件名但页面 img 标签显示裂图。 原因图片写入了磁盘但页面里 img 的 src 写成了相对路径或者图片写入的目录和 JSP 页面实际访问的路径不一致。还有一种情况Eclipse 发布用的是临时部署目录项目重新发布后 upload 文件夹被清空。 解决img 标签里用 ${pageContext.request.contextPath}/upload/文件名 构建完整路径不要写死相对路径。确认发布方式如果图片在临时目录可以考虑把上传目录改成 Tomcat 虚拟映射或者每次部署后重新上传测试图片。5.5 本机只有 JDK 8Tomcat 7 启动失败或工程编译报错现象启动 Tomcat 时控制台报 UnsupportedClassVersionError或者编译直接失败。 原因Tomcat 7 是 JDK 1.6/1.7 时代的产品Eclipse 如果按 JDK 8 的编译级别生成 class 文件Tomcat 7 运行时解析不了高版本字节码。 解决在工程属性里把 Java Compiler 的 compliance level 改成 1.7。如果找不到 1.7 选项要么安装 JDK 1.7 并在 Tomcat 运行配置里指定要么换一个支持 JDK 8 的 Tomcat 版本。这个坑属于环境组合问题和项目代码没关系但十个人里至少有一个人卡在这。6. 验证与进阶两个小改造把项目从「能跑」变成「能交差」项目跑通只是第一步。我交出去之前习惯按固定清单过一遍先用 admin 账号登录进产品管理新增一条带图片的产品翻几页看看分页是否正常搜索一个不存在的关键字看空结果处理再批量删掉测试数据最后退出再直接访问 productList.jsp 验证过滤器把请求拦回了 index.jsp。这套流程走完功能才算真的验收过。如果你想在课程设计答辩或者工作汇报里让它显得更完整我建议做两个小改造。第一个是把 DAO 里的 SQL 全部改成 PreparedStatement重点改登录和分页查询然后把 SQL 拼接相关的代码注释掉。这个动作技术上不难但答辩时老师问“你怎么防 SQL 注入”你就可以指着代码说“所有动态参数都走预编译占位符”。第二个是给 t_user 和 t_product 表加 create_date 默认值产品列表按时间倒序排页面立刻有了“新增的排前面”的顺序感演示时比无序排版好看得多代码改动量也就十几行。我的习惯是拿到任何 JSPServlet 老项目都强制先走一遍“读 SQL → 导工程 → 跑通 → 改功能”的顺序中间不跳步。血泪经验告诉我跳过任何一步后面都是拿时间来填。希望这份 jspservletjdbcmysql 后台管理系统的拆解能帮到你按这个顺序跑一遍半小时内大概率能见到自己的登录界面。本文还有配套的精品资源点击获取
返回列表