ARTICLE DETAIL

资讯详情

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

Java网上银行转账系统源码拆解:JSP+Servlet+JDBC技术栈的事务与安全实践

Java网上银行转账系统源码拆解:JSP+Servlet+JDBC技术栈的事务与安全实践 简介一套基于 Java 与 JavaScript 的网上银行转账系统设计源码面向 JavaWeb 学习者、课程设计与毕业设计开发者可帮助快速搭建具备安全在线转账能力的项目雏形压缩包共 46 个文件主要包括 19 个 Java 源文件、14 个 JSP 页面文件、7 个 XML 配置、2 个属性文件、3 个文本文件及 1 个 JavaScript 脚本整体仅 356KB。Java 源文件承载用户认证、账户管理、资金转入转出、事务控制和错误处理等核心逻辑JSP 页面负责账户信息展示、转账表单与历史记录呈现XML 与属性文件用于配置数据库连接、系统参数JavaScript 脚本强化前端交互与输入校验已有 279 人学习/下载。项目采用 Maven 标准结构附带构建配置、源码目录、资源文件和说明文档便于导入 IDE 直接运行二次开发通过该项目可系统理解 JavaWeb 金融场景下的分层设计、常见安全防护手段和前后端协作方式适合作为课程设计、毕业设计或银行转账类项目的参考蓝本与改造基础。1. 这套网上银行转账系统源码先跑通再谈改造先给结论这套基于Java的网上银行转账系统源码价值不在于「能转账」这个结果而在于它用JSPServletJDBC这条经典技术栈把银行转账的完整链路串了起来。我拆这类课程设计项目时有个习惯先看文件结构再决定从哪下手因为文件类型分布能直接反映作者的分层思路——Java源文件管业务逻辑JSP管页面展示XML和properties管配置JavaScript管前端校验整体是标准的Maven Web工程布局不是随手拼凑的代码碎片。这套源码适合三类人正在做Java课程设计、需要一套能改能讲的项目骨架的学生准备Java面试、想拿事务一致性和并发控制当实战案例的人以及想快速上手老式Java Web项目、搞清楚请求从表单到数据库到底怎么流转的新手。下文沿「文件结构→转账逻辑→安全设计→本地跑通」这条实际拆解路线展开该看的地方讲到参数会坑的地方列成清单。2. 把45个文件拆开看这套源码的分层与请求流转2.1 先看pom.xml和目录骨架Maven项目的地基解压upload.zip之后目录里会看到pom.xml、src/main/resources、src/main/java、src/main/webapp和readme.txt。我拆任何Java Web项目都会先读pom.xml因为它决定了项目的地基依赖了哪些jar包、JDK编译级别是多少、最终打出war还是jar全在这一个文件里写死。读懂了pom.xml后面碰到的ClassNotFoundException大多能提前预判。upload.zip ├── pom.xml # Maven构建配置依赖坐标、插件、打包方式 ├── readme.txt # 部署说明与环境要求 └── src/main ├── resources # 非Java资源目录 │ ├── jdbc.properties # 数据库连接参数 │ └── applicationContext.xml # Spring/MyBatis整合配置 ├── java # 19个Java源文件业务逻辑全部在这 └── webapp # Web应用根目录 ├── WEB-INF │ └── web.xml # Servlet/Filter/Listener注册中心 ├── *.jsp # 14个JSP页面 └── js └── *.js # 1个JavaScript脚本结构说明src/main/java和src/main/webapp是Maven Web项目的标准双目录布局前者放Java源码后者放浏览器能直接访问的资源。WEB-INF目录很特殊它下边的文件浏览器无法直接访问只能通过Servlet的forward()跳转到达这本身就是一道访问控制边界。pom.xml里需要重点核对三处。一是依赖清单里有没有mysql-connector-java、servlet-api、jstl这三个关键包二是maven-compiler-plugin的source和target版本要跟本机JDK一致否则编译直接失败三是packaging标签必须是war因为要部署到Tomcat。readme.txt也别跳过里面通常写了数据库初始化脚本、Tomcat版本要求这类关键信息。2.2 Java源文件的三层划分Servlet、Service、DAO各管一段19个Java源文件是系统的核心。按同类网上银行系统最常见的组织方式它们分四类Servlet控制器、Service业务层、DAO数据访问层和实体Bean。这个分层就是面向对象编程思想在Java Web项目里的落地方案。| 文件类型 | 在系统中的角色 | | Java源文件 | 控制器、业务逻辑、数据访问、实体定义 | | JSP页面 | 页面渲染与用户交互入口 | | XML配置 | 构建描述、Web部署、业务Bean装配 | | 属性文件 | 数据库连接与运行时环境参数外置 | | JavaScript脚本 | 前端表单校验与局部动态交互 |三层之间依赖方向是固定的Servlet依赖ServiceService依赖DAO实体Bean被所有层共用。如果你在项目代码中发现Servlet直接操作JDBC那说明分层已经失守。这套源码从文件类型分布来看是守住了这条边界的。Servlet层做的事情很纯粹取参数、调Service、根据返回值定跳转。Service层承载具体业务规则转账前校验、余额判断、事务控制。DAO层封装JDBC操作向Service返回影响行数或实体对象。这个边界清晰的三层结构后面无论是替换前端框架还是升级后端都留出了改造空间。2.3 一次转账请求的完整链路从浏览器到数据库行锁把前面几个小节串起来一次转账请求的完整流转是这样的transfer.jsp 渲染转账表单 → transfer.js 前端校验 → POST提交到 TransferServlet.doPost() → TransferServlet 解析参数做登录与越权校验 → TransferService.transfer() 开启事务 setAutoCommit(false) → AccountDao.deduct() 扣款 UPDATEWHERE 条件带 balance ? → AccountDao.increase() 入账 UPDATE → 全部成功 commit任一失败 rollback → TransferServlet 跳转 success.jsp 或 error.jsp这条链路每一环都能对应到具体文件中间环节——事务开启、余额条件更新——是技术含量最高的部分。面试讲项目时能把这个流程完整讲清楚比背一百道Java基础面试题都管用。第3章会专门展开这两个点。提示本地搭建环境时先按readme.txt执行数据库脚本再启动Tomcat。如果页面能打开但登录就报错优先检查数据库连接参数是否改成了本机配置。3. 转账核心业务事务、余额校验与BigDecimal3.1 扣款和入账为什么必须在一个事务里ACID的实战含义转账业务最不能接受的结果就是「扣了钱但对方没到账」。A转100元给B正常流程两步先把A的余额减100再把B的余额加100。如果第一步成功、第二步失败A的钱就凭空消失了。数据库事务存在的意义就是消除这种中间状态。这套源码在Service层手动管理事务。具体做法是取数据库连接、关闭自动提交然后依次执行扣款和入账两条SQL全部成功才commit任何一步异常就rollback。核心代码// TransferService.transfer() 手动事务控制关键代码 public boolean transfer(String fromAccount, String toAccount, BigDecimal amount) { Connection conn null; try { conn DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 关闭自动提交开启事务 conn.setAutoCommit(false); // 隔离级别设为读已提交避免读到其他事务的未提交数据 conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); AccountDao dao new AccountDao(conn); // 第一步扣款余额不足时影响行数为0 int deductRows dao.deduct(fromAccount, amount); if (deductRows 0) { conn.rollback(); return false; } // 第二步入账收款账户不存在时也回滚 int increaseRows dao.increase(toAccount, amount); if (increaseRows 0) { conn.rollback(); return false; } // 两笔操作都成功提交事务 conn.commit(); return true; } catch (Exception e) { // 任何异常都回滚保证扣款与入账的原子性 conn.rollback(); throw new RuntimeException(转账异常事务已回滚, e); } finally { // 恢复自动提交、关闭连接这是最容易被漏掉的一步 conn.setAutoCommit(true); conn.close(); } }逻辑说明这段代码的四个关键点——setAutoCommit(false)必须放在第一条SQL之前两个DAO操作都要检查影响行数catch中回滚保证异常不落库finally里恢复自动提交再关闭连接。最后一点特别容易漏如果连接来自数据库连接池不恢复自动提交就归还下一个请求拿到这条连接会一直不提交事务数据写不进去还特别难排查。参数说明TRANSACTION_READ_COMMITTED是数据库隔离级别保证只读取已提交的数据。MySQL默认是REPEATABLE_READ在当前场景下也能工作但在代码里显式声明可以让读者一眼明白事务边界内的可见性预期。3.2 余额不足的拦截条件更新比先查后扣可靠得多写余额校验新手最常用的写法是「先SELECT查余额再判断够不够够了就UPDATE」。这种写法在低并发下没问题一旦并发就翻车两个请求同时查到余额100元都判断余额充足各自扣80元最后账户余额变成-60元。这就是经典的超扣问题。可靠做法是把余额条件写进UPDATE的WHERE子句让数据库在更新时做最终判断// AccountDao.deduct() 余额条件更新 public int deduct(String accountId, BigDecimal amount) throws SQLException { // 关键在于 balance ? 条件不满足时影响行数为0 String sql UPDATE account SET balance balance - ? WHERE account_id ? AND balance ?; PreparedStatement ps conn.prepareStatement(sql); ps.setBigDecimal(1, amount); // 扣款金额 ps.setString(2, accountId); // 转出账户ID ps.setBigDecimal(3, amount); // 余额下限条件 return ps.executeUpdate(); }逻辑说明影响行数是关键信号。余额充足时返回1不足时返回0。两条并发请求同时扣款时数据库行锁会让它们排队执行后执行的那条因余额不足返回0Service层拿到0就回滚负数余额从源头被堵死。这个写法直接回答了「Java怎么保证数据一致性」这个问题事务保证原子性条件更新保证余额约束行锁保证并发安全。拿这套源码里的实际写法举例比背概念有说服力得多。| 方案 | 并发安全性 | 实现复杂度 | 适用场景 | | 先查后扣 | 低会超扣 | 低 | 单用户演示 | | 条件更新 行锁 | 高 | 中 | 真实转账场景 |3.3 金额计算必须用BigDecimal精度损失是硬伤银行系统的金额字段有一条铁律不用浮点类型。double和float在二进制里无法精确表示0.1、0.2这种小数0.1加0.2算出来是0.30000000000000004。单个交易看是分位误差累计下来就是账目对不上。金额的正确类型分两层Java代码里用BigDecimal数据库里用DECIMAL。用BigDecimal还有一个容易踩的坑——构造时尽量传字符串new BigDecimal(100.00)是对的new BigDecimal(100.00)又绕回了浮点精度问题。项目里如果发现账户余额出现尾数异常先排查有没有人用double做了金额计算。4. 安全防线登录认证、SQL注入与CSRF防御4.1 登录认证与会话管理session与密码存储的基本功网上银行系统的第一道门是登录。JSPServlet架构下常规流程是登录表单提交到LoginServletServlet校验用户名密码成功后把用户ID写入session之后每个需要登录的接口在入口处检查session状态。这里有两个不能偷懒的地方。第一个是后端必须校验登录态前端隐藏登录页按钮不算认证。第二个是session超时时间必须配置银行类系统的会话空闲超时一般控制在15到30分钟!-- web.xml 配置会话超时时间 -- session-config !-- 单位为分钟 -- session-timeout30/session-timeout /session-config配置说明30分钟表示用户30分钟内无任何操作服务器自动销毁session下次请求强制重新登录。这个参数如果不配置Tomcat虽然有默认值但显式声明是安全设计的一部分面试时提到这一点能体现出系统性思维。密码存储也是一个绕不开的话题。课程设计项目里最常见的是MD5或SHA-1直接哈希这在彩虹表面前等于明文。如果这份源码用的是MD5建议至少改成加盐哈希每个用户随机生成salt密码存储为hash(password salt)。更进一步可以用BCrypt它的设计目标就是抗暴力破解。4.2 防SQL注入与XSS输入输出两端的边界前面代码里已经用过PreparedStatement的占位符这是防SQL注入的第一道防线。它的原理是参数化查询数据库把?位置的内容当作纯数据解析而不是可执行的SQL片段。需要警惕的是项目里有没有其他地方用字符串拼接SQL搜索Java源文件里的号和String.format凡是在SQL语句里拼条件的地方都值得停下来看一眼。XSS则是另一条攻击路径攻击者把恶意脚本写进转账备注这类字段受害者查看交易记录时脚本在浏览器里执行可能盗取cookie或篡改页面内容。防御手段分输入和输出两端输入端过滤script、onerror这类危险标签输出端做HTML转义。JSP页面里用JSTL的c:out输出用户内容它默认转义HTML比直接用${}输出安全。这套源码里的JSP页面如果大量使用EL表达式直接输出参数建议统一替换成c:out。4.3 越权与CSRF转账接口最容易漏掉的防御点转账系统里最隐蔽的风险是越权攻击者不需要攻破管理员密码登录一个普通账户后构造请求就能操作不属于自己的账户。防御的关键是转账接口必须校验「当前登录用户」和「转出账户」属于同一个主体// TransferServlet 中的越权校验片段 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session request.getSession(false); if (session null) { response.sendRedirect(login.jsp); return; } User loginUser (User) session.getAttribute(loginUser); String fromAccount request.getParameter(fromAccount); // 越权校验转出账户必须属于当前登录用户 if (loginUser null || !loginUser.getAccountId().equals(fromAccount)) { response.sendRedirect(transfer.jsp?errorillegalAccount); return; } // 校验通过后才执行后续转账逻辑 }逻辑说明这段代码把转出账户绑定到session里的登录用户非本人账户的转账请求直接拦截。这个判断写不写决定了系统是「登录就能用」还是「只能操作自己的账户」也是面试里「接口越权」这一考点的核心。CSRF的攻击场景是用户登录银行系统后cookie仍在有效期此时访问恶意网站恶意页面里的表单自动向银行系统的转账接口提交请求浏览器自动带上银行cookie转账就这样被执行了。防御主流做法是转账表单里埋一个一次性token后端校验通过才处理。这套源码如果没做CSRF防护面试时可以主动提出能自己补这个能力反而是一个加分点。提示SQL注入、XSS、CSRF这三类攻击在Java面试中属于高频考点。对照源码逐一确认防御措施是否到位缺哪个补哪个是一个非常扎实的练习项目。5. 本机跑通排查5个最常见的翻车点与处理办法5.1 现象Tomcat启动时直接报ClassNotFoundException原因多数是pom.xml里依赖缺失或jar包没有打进WEB-INF/lib。报错集中在三个类上——com.mysql.jdbc.Driver、javax.servlet.ServletException、org.apache.jasper.JasperException。前两个是依赖问题最后一个多半是编译期少引了jstl或servlet-api。少数情况是IDE里Maven依赖没刷新代码里一片飘红。解决先执行mvn clean package看能否打包再检查pom.xml里依赖的groupId、artifactId、version三个坐标。注意MySQL 8之后驱动的groupId变成了com.mysql老坐标会冲突。最后看war包里WEB-INF/lib目录有没有对应jar没有就检查依赖的scope是否写成了provided。5.2 现象页面能打开但显示的全是乱码原因请求编码、响应编码、数据库编码三条链路不一致。典型组合是Tomcat默认用ISO-8859-1接收POST参数而JSP页面是UTF-8两边一碰中文就全乱。数据库表如果是latin1编码存进去再读出来照样是乱码。解决web.xml里配置CharacterEncodingFilter把所有请求和响应统一为UTF-8。JSP页面顶部声明pageEncoding数据库连接URL追加?useUnicodetruecharacterEncodingutf8。这三处统一之后乱码问题基本消失。5.3 现象转账成功后按刷新按钮钱被转走了两次原因浏览器刷新时重发了上一次的POST请求后端没做防重处理第二次请求把转账流程又完整执行了一遍。这类问题在资金类系统里非常致命也是面试里「接口幂等性」的典型场景。解决最规范的做法是Post/Redirect/Get模式——转账成功后在Servlet里用response.sendRedirect跳转结果页而不是forward到JSP。redirect让浏览器重新发GET请求刷新时刷的是结果页URLPOST不会被重放。再在前端加一道保险提交后把转账按钮置灰用户连续点击也只发一次请求。5.4 现象部署到服务器后MySQL报Communications link failure原因多数不是代码问题而是网络层问题。服务器防火墙没放行3306端口或MySQL的bind-address配置只允许本机连接。我第一次部署这类项目就栽在这调了一下午代码最后发现防火墙规则漏了一条。解决在服务器本机执行mysql -u root -p确认服务正常再用telnet 服务器IP 3306测端口连通性。不通就放行防火墙端口。MySQL 8还要在连接URL上加serverTimezoneAsia/Shanghai否则驱动会报时区错误。5.5 现象MySQL驱动报Public Key Retrieval is not allowed原因MySQL 8的驱动默认不允许客户端从服务器检索RSA公钥导致连接被拒。网上搜到的方案很多让改服务器配置对本地开发来说改连接URL更轻量。解决JDBC连接串追加allowPublicKeyRetrievaltrueuseSSLfalse连接即恢复。注意useSSLfalse只建议在本地开发环境使用生产环境应当启用SSL加密链路。这也呼应了银行系统「传输层加密」的基本要求。这五个坑是从零跑通这类项目的血泪经验前三个本地开发阶段就会遇到后两个是部署到服务器才暴露。如果按readme.txt走完仍不通优先按四个维度排查依赖、编码、数据库参数、防火墙。6. 进阶改造从Servlet手写事务到Spring Boot迁移这套源码用JSPServlet手写事务逻辑完整但工程效率偏低。想把它升级成能写进简历的项目建议按两个方向改。第一个方向保持原有分层不变把Service层手动事务换成Spring的Transactional注解DAO层换成Spring JDBC Template或MyBatisXML配置保留但能用注解替代就替代。第二个方向整体迁移到Spring BootSpring MVC替换Servlet数据访问换成MyBatis-Plus或JPA。迁移时三层边界不要动Servlet的角色交给ControllerService和DAO层代码基本可以平移。迁移过程中有一个高频翻车点手动事务换成Transactional后默认只在RuntimeException时回滚受检异常不会触发回滚和老代码「任何异常都回滚」的行为不一致。代码平移前必须把rollbackFor设为Exception.class否则扣款成功、入账失败时事务可能不提交回滚资金就出问题了。这类事故在Spring迁移案例里很常见我当年就吃过亏。从那以后每次做这类迁移我都会用一个故意的坏SQL做回滚验证第一步把入账表名改错第二步执行转账确认事务回滚第三步查库里确认扣款记录不存在。这个「破坏性验证」的习惯被我固化成流程了。希望帮到你。本文还有配套的精品资源点击获取
返回列表