简介:一份基于ASP技术的学校学生报名缴费管理系统源码,面向Web开发初学者、ASP工程师以及学校信息化系统设计人员。资源以学生报名、缴费管理为核心,覆盖用户登录与权限控制、Access数据库设计、课程报名、在线支付集成思路、报名缴费状态跟踪、统计报表、安全防护等完整业务模块,适合用来理解数据驱动Web应用的开发流程。
压缩包共147个文件,大小559KB。其中48个ASP文件实现核心业务逻辑,1个MDB文件为Access数据库,13个JS与11个CSS负责前端交互与样式,其余GIF/PNG/JPG为界面图片素材,另有TXT、FIX等辅助说明文件。压缩包体积小,目录结构清晰,便于按模块研读。
已有152人学习下载。通过阅读源码,可掌握经典ASP与Access数据库的交互方式、角色权限控制、报名缴费状态设计,以及后台管理界面和统计报表的实现思路;对于想要了解学校管理信息化系统的人来说,是一个能快速上手且信息量适中的学习实例。
1. 开学前两周,一份压在学校服务器里的 ASP 源码
每年八月下旬,教务处老师会拿来一个 zip——学校学生报名缴费管理系统的 ASP 源码,外加一个 Access 数据库和一张手写说明。系统看着又老又土,处理的却是几百号学生的报名、收费项目匹配、缴费流水登记和退费冲销,金额算错或重复入账,第二天就得对着纸质收据手工补。这类源码通常是纯 VBScript 加 ADO,页面文件散在十几个目录,登录、报名、缴费、查询各管一段,几乎没有分层。让它稳定跑起来,难点不在界面,而在三件事:IIS 的 ASP 运行环境配没配对、数据库连接与表结构读没读懂、缴费金额和状态在并发窗口里守不守得住。下面按部署、读源码、做一致性、收口验收四条线,把它从「能打开」推到「能收钱」。
2. Win11 配置 IIS 跑 ASP 源码:完整链路与 80004005 排错
2.1 用 DISM 一条条补齐 IIS 与 ASP 支持
Win11 默认装的是 IIS 的壳,经典 ASP 这个功能是关着的,直接把源码丢进去只会得到 404,或者浏览器把 .asp 当纯文本吐出来。图形化路径是「控制面板 → 程序 → 启用或关闭 Windows 功能 → Internet Information Services → 万维网服务 → 应用程序开发功能」,把 ASP、ISAPI 扩展、ISAPI 筛选器三项都勾上。要批量部署或远程操作,用 DISM 更省事:
:: 以管理员身份运行 CMD 或 PowerShell dism /online /enable-feature /featurename:IIS-WebServerRole /all /norestart dism /online /enable-feature /featurename:IIS-WebServer /all /norestart dism /online /enable-feature /featurename:IIS-CommonHttpFeatures /all /norestart dism /online /enable-feature /featurename:IIS-StaticContent /all /norestart dism /online /enable-feature /featurename:IIS-DefaultDocument /all /norestart :: 下面这条才是经典 ASP 的本体,缺了它 .asp 不会被执行 dism /online /enable-feature /featurename:IIS-ASP /all /norestart dism /online /enable-feature /featurename:IIS-ISAPIExtensions /all /norestart dism /online /enable-feature /featurename:IIS-RequestFiltering /all /norestartIIS-WebServerRole是父级角色,/all会连带装上父节点的依赖项;IIS-ASP装的是 asp.dll 扩展和 ASP 配置节;IIS-ISAPIExtensions决定 IIS 管理器里能不能开关 ISAPI 程序。/norestart只是不让系统自动重启,功能本身即时生效。装完在 IIS 管理器点站点根节点,右侧能看到「ASP」图标,双击进去按需设置「启用父路径」和「调试属性 → 将错误发送到浏览器」。
还有一步经常被漏:确认站点的默认文档列表里有 index.asp 和 default.asp。目录首文件叫 index.asp、而默认文档里没有它,访问根路径就是 403 或 404,跟 ASP 功能本身没关系。
2.2 应用程序池要不要开 32 位,取决于数据库驱动
经典 ASP 走 ADO,Access 用的 Jet 引擎只有 32 位版本。Win11 的 IIS 默认跑在 64 位应用程序池上,这时Provider=Microsoft.Jet.OLEDB.4.0会直接报「未找到提供程序。该程序可能未正确安装」。两个解法:把应用池切成 32 位,或者换Microsoft.ACE.OLEDB.12.0并装对应位数的 Access Database Engine。
我一般直接开 32 位,因为老源码里 Jet 连接串到处都是,逐个改容易漏。命令行:
%windir%\system32\inetsrv\appcmd set apppool "DefaultAppPool" /enable32BitAppOnWin64:true %windir%\system32\inetsrv\appcmd set app "Default Web Site/" /applicationPool:"DefaultAppPool" iisresetenable32BitAppOnWin64:true让工作进程以 32 位模式加载 ISAPI;appcmd set app确保站点确实挂在改过设置的那个池上——一台机器上跑多个站点时,改错池子等于没改。如果拿到的源码是 .aspx 而不是 .asp,那是 ASP.NET,处理思路完全不同,比如 ASP:GridView 列里的数据怎么换行,走的是模板列加<br/>或 Eval 绑定的路子,别和这里的 ADO 问题混着排查。
2.3 连接字符串与目录权限:0x80004005 的四个高发原因
连接字符串通常藏在 conn.asp 或 inc/ 目录的公共文件里,形如:
<% ' conn.asp —— 全站公共数据库连接 Dim ConnStr, Conn ConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & Server.MapPath("/data/school.mdb") & ";" & _ "Jet OLEDB:Database Password=" & Application("DbPwd") & ";" Set Conn = Server.CreateObject("ADODB.Connection") Conn.Open ConnStr %>Server.MapPath把虚拟路径转成物理路径,避免相对路径在不同虚拟目录层级下失效。Jet OLEDB:Database Password是 Access 的数据库密码,不是登录用户的密码,两个别写反。数据库放站点外,要给应用池标识读权限;放站点内还要额外给写权限,因为 Access 会生成 .ldb 锁文件。
权限用 icacls 一条命令处理:
icacls "D:\apps\schoolpay\data" /grant "IIS_IUSRS:(OI)(CI)M" /T(OI)(CI)表示对象继承与容器继承,M是修改权限,/T递归到子目录。老教程里让加 Everyone 读写,这个做法现在不该再用。
高频报错对照:
| 报错信息 | 根本原因 | 处置 |
|---|---|---|
| ADODB.Connection 错误 '800a0e7a' 未找到提供程序 | 64 位应用池加载 32 位 Jet | 应用池启用 32 位应用程序 |
| Provider for ODBC Drivers 错误 '80004005' 未指定的错误 | 数据库目录缺写权限,.ldb 建不出来 | icacls 授予 IIS_IUSRS 修改 |
| 80040e14 SQL 语句中出现语法错误 | 拼接 SQL 撞上空值或单引号 | 参数化查询 + 前置 Trim 判空 |
| 800a000d 类型不匹配 | 表单空串直接传给 CLng/CDbl | IsNumeric 判断后再转换 |
| 80004005 操作必须使用一个可更新的查询 | 目录只读或数据库被本地 Access 占用 | 关闭本地 Access,补写权限 |
注意:调试期把「将错误发送到浏览器」设为 True 能看清行号,上线前必须改回 False,否则数据库物理路径和字段名会直接暴露给访问者。
3. 拆开报名缴费源码:表结构、文件分工与缴费写入逻辑
3.1 目录骨架:先认出哪个文件在管缴费
拿到源码别急着改,先 tree 一遍。典型结构如下:
/schoolpay /admin 后台:班级管理、收费项目、操作员 /data school.mdb、备份目录 /inc conn.asp、checkuser.asp、fun.asp /student 前台:注册、报名、查询 pay.asp 缴费录入页面 pay_save.asp 缴费写库处理页 refund.asp 退费冲销 print.asp 收据打印 export.asp 花名册与流水导出 /upload 照片、证明材料命名规律基本是「页面名 + _save / _do / _action」的写库组合,看后缀就能判断哪个是纯展示、哪个会改数据。inc/checkuser.asp一般通过<!--#include file="checkuser.asp"-->被所有后台页面包含,负责 Session 校验,是整个权限体系的入口。先把这几个文件读一遍,比通读全部页面高效得多。
3.2 四张核心表:报名、收费项目、缴费流水、操作员
打开 mdb,能撑起业务的表通常只有四张,字段名各家不同但语义一致:
| 表名(示例) | 关键字段 | 说明 |
|---|---|---|
| t_student | stu_no, stu_name, grade, class_name, phone, status | 学生主档,stu_no 是业务主键,别用自增 id 去关联 |
| t_feeitem | item_id, item_name, grade, term, amount, must_pay | 收费项目,amount 是应收金额的唯一权威来源 |
| t_payment | pay_id, stu_no, item_id, term, should_pay, real_pay, pay_type, pay_time, operator, state | 缴费流水,应收与实收分开存是退费和差额对账的基础 |
| t_user | user_name, pwd, role | 操作员与权限,role 决定能进哪些后台页面 |
有两处必须盯住。第一,t_feeitem里同一项目在不同年级金额可能不同,取值时必须同时带 grade 和 term 条件,只按 item_id 取会串年级。第二,t_payment的 state 字段如果只有 0/1,退费就只能靠再插一条负数流水实现,对账时记得把负数算进去,不能只 SUM 正数。
3.3 源码里缴费写库的原始写法与三个隐患
大部分这类源码的 pay_save.asp 长这样:
<% ' pay_save.asp —— 原始写法,仅用于说明问题 Dim stuNo, itemId, money, payType stuNo = Trim(Request.Form("stu_no")) itemId = Trim(Request.Form("item_id")) money = Trim(Request.Form("money")) ' 隐患一:金额来自客户端 payType = Trim(Request.Form("pay_type")) Conn.Execute "INSERT INTO t_payment(stu_no,item_id,should_pay,real_pay,pay_type,pay_time,state) " & _ "VALUES('" & stuNo & "'," & itemId & "," & money & "," & money & ",'" & payType & "',Now(),1)" Response.Write "<script>alert('缴费成功');location.href='pay.asp';</script>" %>三个隐患都挤在同一行。money直接来自表单,前端隐藏域改成 1 就能一块钱缴完;stu_no和payType是字符串拼接,带单引号就报错,带' or '1'='1就是注入;没有事务、没有唯一约束,连点两下提交就是两条流水。这三条在下一章逐个处理,先把它们记下来。
3.4 在源码上新增一个收费项目要改哪几处
新增「校服费」这类项目,不只是往 t_feeitem 插一行。要动的地方按顺序走:t_feeitem 插记录(确定 grade 和 term);后台收费项目管理页的下拉框是读全表还是写死了几个 item_id;pay.asp 缴费录入页有没有硬编码项目列表;打印模板 print.asp 里的项目名称来自数据库还是写死文案;导出报表的列头有没有跟着加。
先跑这一句确认数据:
-- 确认项目插入后能被正确取到,注意必须带 grade 和 term SELECT item_id, item_name, amount FROM t_feeitem WHERE grade = '2024' AND term = '2024-2025-1' AND must_pay = 1;如果这条查得出、页面上却没有,说明是页面把列表写死了,改页面而不是改库。grade用字符串还是数字、term用什么格式,要跟源码里已有写法保持一致,混用会让后面所有 WHERE 条件失配。
4. 缴费金额与状态一致性:服务端重算、幂等令牌与对账
4.1 只改一个隐藏域就能少缴几百块
打开缴费页面按 F12,能看到表单里有个<input type="hidden" name="money" value="680">。右键改成 1 再提交,如果服务端不重算,数据库里就真记了 1 元。这不是理论漏洞,是这类 ASP 源码最常见的投诉来源,而且查起来很麻烦——流水是真的,页面上「缴费成功」也弹了,只有月底对账才发现总额对不上。
判断一份源码有没有这个问题的办法很直接:搜 pay_save 里 INSERT 语句的取值来源,只要金额、应收金额、缴费状态这三项里任何一项来自Request.Form,就必须重算。
4.2 服务端重算应收金额的 ASP 写法
正确做法是客户端传什么金额都不采信,只用它做展示比对,真正的应收从 t_feeitem 里查:
<% ' pay_save.asp —— 服务端重算应收 Dim stuNo, itemId, gradeStr, termStr, shouldPay stuNo = Trim(Request.Form("stu_no")) itemId = CLng(Request.Form("item_id")) gradeStr = Trim(Request.Form("grade")) termStr = Trim(Request.Form("term")) ' 1) 基本格式校验,挡掉空串和明显异常的学号 If Len(stuNo) < 4 Or Len(stuNo) > 20 Then Response.End If Not IsNumeric(itemId) Then Response.End ' 2) 从库中取应收金额,带 grade 和 term,避免串年级 Dim rsFee Set rsFee = Conn.Execute("SELECT amount FROM t_feeitem WHERE item_id=" & itemId & _ " AND grade='" & gradeStr & "' AND term='" & termStr & "'") If rsFee.EOF Then Response.End shouldPay = CDbl(rsFee("amount")) rsFee.Close %>Len限制学号长度是为了挡掉超长畸形输入;CLng前先判断IsNumeric,否则空表单会抛 800a000d 类型不匹配。shouldPay从此成为这一笔流水的唯一应收值,后面无论前端传什么,入库都用它。这一版仍然是拼接 SQL,第 5 章会把它改成参数化。
4.3 幂等令牌加唯一索引压住重复提交
服务端重算解决了改金额,重复提交还得单独处理。两层防护一起上:页面层发一次性令牌,数据库层加唯一约束。
<% ' token.asp —— 生成一次性缴费令牌,塞进缴费表单的隐藏域 Function MakeToken() Dim t Randomize t = CStr(Int(Rnd * 900000) + 100000) & CStr(Timer * 1000) t = Replace(Replace(t, ".", ""), " ", "") Session("pay_token") = t MakeToken = t End Function %>在 pay_save.asp 开头校验:
<% Dim clientToken clientToken = Trim(Request.Form("pay_token")) If clientToken = "" Or clientToken <> Session("pay_token") Then Response.Write "重复提交,请返回缴费页面重试" Response.End End If Session("pay_token") = "" ' 用掉即作废 %>Session("pay_token")是同一个会话里的最后有效令牌,比对通过后立刻清空,刷新或回退再提交就会命中第二个分支。数据库侧的兜底:
-- 一个学生、一个项目、一个学期只允许一条有效流水 ALTER TABLE t_payment ADD CONSTRAINT ux_pay_once UNIQUE (stu_no, item_id, term);有了这条约束,即使两个人同时在两台机器上操作,第二条 INSERT 也会直接失败,捕获错误后提示「该生本学期该项目已缴费」。退费不要新插一条流水绕开约束,应该在原记录上更新 state 并写入退费字段。
4.4 每日对账 SQL 与差异定位
开学高峰期一天几百笔,靠人工翻页面不现实,两条查询就能覆盖日常核对:
-- 按日汇总,看每天收了多少笔、多少钱 SELECT DateValue(p.pay_time) AS 缴费日期, COUNT(*) AS 笔数, SUM(p.real_pay) AS 实收合计 FROM t_payment p WHERE p.state = 1 GROUP BY DateValue(p.pay_time) ORDER BY 1 DESC;-- 定位实收与应收不一致的记录 SELECT p.stu_no, p.item_id, p.real_pay, f.amount FROM t_payment p INNER JOIN t_feeitem f ON p.item_id = f.item_id WHERE p.real_pay <> f.amount AND p.state = 1;DateValue把带时间的 pay_time 截成日期,Access 的 GROUP BY 才分得干净。第二条是核心:正常情况下一行都查不出来,有记录就说明存在改金额、优惠减免未登记或项目金额中途调整,逐条追到操作员即可。查询时注意f.item_id可能对应多个年级,如果同一项目跨年级金额不同,第二条要补上 grade 和 term 的关联条件。
5. 上线前的收口:参数化查询改造、导出打印与验收清单
5.1 把拼接 SQL 全换成 ADODB.Command 参数化
金额重算之后还剩注入问题。手工转义单引号容易漏,正确的做法是用 Command 对象的参数集合:
<% Dim cmd, rsFee2 Set cmd = Server.CreateObject("ADODB.Command") Set cmd.ActiveConnection = Conn cmd.CommandType = 1 ' adCmdText cmd.CommandText = "SELECT amount FROM t_feeitem WHERE item_id=? AND grade=? AND term=?" cmd.Parameters.Append cmd.CreateParameter("item_id", 3, 1, , itemId) ' adInteger cmd.Parameters.Append cmd.CreateParameter("grade", 202, 1, 10, gradeStr) ' adVarWChar cmd.Parameters.Append cmd.CreateParameter("term", 202, 1, 20, termStr) ' adVarWChar Set rsFee2 = cmd.Execute %>参数说明:第二个参数是数据类型,3 是 adInteger,202 是 adVarWChar,5 是 adDouble,6 是 adCurrency;第三个参数 1 表示 adParamInput;第四个参数是长度,变长字符串必须给足,给小了会被静默截断,这种错误不报异常但数据是错的。改造顺序建议从 pay_save.asp、login.asp、查询页这三个输入最集中的文件开始,改完之后用' or '1'='1和%各测一遍。
5.2 花名册导出 Excel 与中文乱码
这类源码的导出多数是把 HTML 表格伪装成 xls,Excel 能正常打开,改造成本最低:
<% Response.CodePage = 936 Response.Charset = "GB2312" Response.ContentType = "application/vnd.ms-excel" Response.AddHeader "Content-Disposition", "attachment;filename=payment_" & Replace(CStr(Date()),"/","") & ".xls" %> <table border="1"> <tr><th>学号</th><th>姓名</th><th>项目</th><th>实收</th><th>缴费时间</th></tr> </table>中文乱码的两个来源:一是 Response.CodePage 和数据库里的编码不一致,Access 中文版配合 GB2312 最稳;二是文件名带中文,部分浏览器会截断或乱码,所以文件名只用英文和日期。如果导出的数字列在 Excel 里变成文本、不能求和,在单元格上补style="mso-number-format:'0\.00'"即可。
5.3 上线前逐项打勾的验收清单
| 检查项 | 合格标准 | 验证方式 |
|---|---|---|
| ASP 执行 | .asp 不再以文本形式返回 | 访问任意 .asp 页面看是否执行 |
| 应用池位数 | 与数据库驱动位数匹配 | 缴费页能正常写库 |
| 数据库权限 | data 目录可写 | 提交一笔后检查是否生成 .ldb |
| 金额重算 | 改隐藏域金额后入库仍为应收值 | F12 改值提交,查流水 |
| 重复提交 | 连点两次只产生一条流水 | 快速双击提交按钮 |
| 注入防护 | 学号输入' or '1'='1不报错不返回数据 | 登录框与查询框各测一次 |
| 错误显示 | 前台不出现 SQL 语句和路径 | 故意访问不存在的记录 |
| 导出编码 | 导出的 Excel 中文不乱码 | 打开文件核对列头 |
| 备份策略 | 每日自动复制一份 mdb 到独立目录 | 查看备份目录时间戳 |
清单里最容易过不了的是「重复提交」和「错误显示」两项,前者靠第 4 章的令牌加唯一索引,后者只需要把 ASP 配置里的「将错误发送到浏览器」关掉。把「按日汇总」那条查询挂进每天早上的巡检任务,开学高峰期一天跑一次,比事后翻几百条流水快得多。
本文还有配套的精品资源,点击获取