ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

JavaWeb学生成绩管理系统课设全解析:从环境配置到简历改造

2026/10/8 14:35:33 拓冰建站 浏览量
JavaWeb学生成绩管理系统课设全解析:从环境配置到简历改造 简介这是一套基于JavaWeb的学生成绩管理系统项目源码与配套数据库面向计算机专业正在完成课程设计、期末大作业的本科生以及需要Web开发实战练习的入门学习者。系统覆盖前端页面、后端业务逻辑、数据库脚本与配置文档包含学生信息管理、成绩录入统计、教师学生角色区分等典型功能曾作为大三期末大作业获得导师指导与98分评审成绩可作为课程设计高分参考。资源共353个文件压缩包约9.85兆字节文件构成以Java源码、JSP动态页面、CSS样式、JavaScript脚本及数据库脚本为主并含class编译文件和jar依赖库方便导入数据库后部署运行。目前已有810人学习。读者可参照项目数据访问层、业务层和控制层的分层实现快速搭建同类管理系统借助附带数据库文件完成数据初始化还能学习登录验证、成绩查询、前后端交互联调及常见乱码与连接配置问题的排查思路。1. 期末大作业98分的学生成绩管理系统它到底值不值得照着做一到期末JavaWeb课程设计群里问得最多的就是这套学生成绩管理系统标题带了“源码数据库”还标了98分。这类项目确实是JavaWeb教学里最典型的一整套闭环——JSP页面写前端、Servlet接请求、JDBC连MySQL再加一张规范的成绩表基本上把课上讲过的知识点全部串了一遍。对正在赶课设的学生来说它是可以直接运行的完整参照对想补项目经验的初学者来说它又是一份能读懂、能改、能讲清楚的标准范本。这篇笔记就按我平时帮人跑这类项目的顺序从环境配置、数据表设计、增删改查链路到高频翻车点把整条路线拆开讲透。2. 先把环境跑通JDK、Tomcat、MySQL的版本配对与导入流程2.1 版本怎么选JDK8 Tomcat9 MySQL 8.0 是少踩坑的组合很多同学拿到源码包第一件事就是双击打开然后被各种报错劝退。问题多数不在代码而在版本组合。我见过太多次“JDK 17 跑 JSP 项目直接报错”的情况原因不是代码烂而是Tomcat和Servlet API的版本压根不匹配。常见的做法是保持教学环境的老三样JDK 1.8、Tomcat 9.0、MySQL 8.0。JDK 8 对老项目的兼容性最好Tomcat 9 对应Servlet 4.0规范能同时覆盖大部分课设代码里javax.servlet的包路径MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver这点要注意因为MySQL 5.x时代用的是com.mysql.jdbc.Driver如果你的JDBC配置里还是老写法会在启动时直接抛ClassNotFoundException。组件推荐版本说明JDK1.88u202及以后老项目兼容性最好避免17以上模块化限制Tomcat9.0.xServlet 4.0兼容绝大多数课程设计MySQL8.0.x主流教学版本注意驱动类名与时区参数Maven3.6.x如果源码带pom.xml用它管理依赖更省心如果源码里带的是lib文件夹而不是Maven坐标千万别把JDK和Tomcat的lib混在一起我后面会专门讲这个坑。2.2 导入IDEA普通Web项目与Maven项目的处理差别先看源码根目录有没有pom.xml。有就走Maven导入IDEA里File - New - Project from Existing Sources选中pom等待依赖下载完成后在Project Structure里把Artifact改成war exploded再配置Tomcat。没有pom说明是传统Web项目需要手动把lib目录里的jar包加到Project Structure的Libraries面板。导入成功后注意一个关键动作打开Project Structure - Artifacts确认WEB-INF/lib下能看到项目的全部依赖。这一步漏了Tomcat启动后所有用到JDBC或Servlet API的类都会报NoClassDefFoundError。对于传统项目IDEA不会自动把外面的lib包进war包必须手动确认。下面是一个最小可用的pom.xml片段如果源码里没有Maven配置可以自己补上dependencies !-- Servlet APITomcat 9对应4.0 -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- JSP API -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- MySQL驱动8.x对应cj驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.28/version /dependency !-- JSTLJSP页面里做循环和判断常用 -- dependency groupIdjstl/groupId artifactIdjstl/artifactId version1.2/version /dependency /dependencies注意依赖的scopeServlet和JSP API必须设为provided因为Tomcat自身带了这些jar如果打成war包时重复打包会产生类冲突。JDBC驱动设为默认的compile即可Java 11以上用MySQL 8.0驱动不需要额外做模块化处理但JDK1.8也完全够用。2.3 数据库初始化从SQL脚本到本地实例的导入步骤源码包里一般会附带一个score.sql或students.sql文件。拿到后先不要急着一股脑导入建议用记事本打开扫一眼确认三件事建库语句是否带utf8mb4字符集、是否有DROP DATABASE IF EXISTS、表引擎是不是InnoDB。很多课设源码里的脚本是从老库导出来的注释和句式可能很乱但并不影响执行。推荐用命令行导入避免图形化工具因编码导致的中文乱码。打开终端执行mysql -u root -p --default-character-setutf8mb4 # 输入密码后在MySQL客户端里执行建库和导入 source E:/path/to/score.sql;--default-character-setutf8mb4这个参数非常重要。如果脚本文件是UTF-8编码但客户端默认用latin1去读导入后所有中文都会变成问号。MySQL 8.0默认字符集为utf8mb4但终端会话的字符集不一定是显式指定了之后表里的中文备注和初始数据不会翻车。导入完成后执行show tables;检查表是否齐全再执行select * from student;看看初始数据是否正常。如果命令提示找不到文件优先检查路径里有没有空格Windows下最常见的错误就是路径中含中文或空格没有加引号。到这里环境就绪了接下来要看数据库的表到底是怎么设计的。3. 数据模型设计四张表如何支撑整个成绩管理流程3.1 从结构上看清学生、课程、成绩三张核心表的关系一个学生成绩管理系统业务上绕不开三样东西学生、课程、成绩。大多数课设代码里对应三张表外加一张用户表用于登录。这四张表的关系很标准学生和课程是多对多成绩表就是它们的关联表同时存储分数和可能的考试类型。先看学生表和课程表字段通常不会复杂CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 学生ID, sno VARCHAR(20) UNIQUE NOT NULL COMMENT 学号, sname VARCHAR(50) NOT NULL COMMENT 姓名, gender CHAR(1) DEFAULT 男 COMMENT 性别, age INT COMMENT 年龄, class_name VARCHAR(100) COMMENT 班级, created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表; CREATE TABLE t_course ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 课程ID, cno VARCHAR(20) UNIQUE NOT NULL COMMENT 课程编号, cname VARCHAR(50) NOT NULL COMMENT 课程名称, credit DECIMAL(3,1) DEFAULT 2.0 COMMENT 学分, teacher VARCHAR(50) COMMENT 任课教师 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表;这里用AUTO_INCREMENT做主键同时给业务字段学号、课程编号加UNIQUE约束。课设系统里经常出现重复导入的问题唯一约束能让重复数据在写入时被数据库挡住而不是靠Java代码去判重。成绩表是关联核心CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2) COMMENT 成绩保留两位小数, exam_date DATE COMMENT 考试日期, UNIQUE KEY uk_stu_course (student_id, course_id), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES t_student(id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES t_course(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT成绩表;UNIQUE KEY uk_stu_course保证同一门课同一名学生只有一条记录这是防止成绩重复录入的数据库层防线。外键约束在这里不是摆设如果先删学生表的学生A而成绩表里还有A的记录InnoDB会直接报错防止产生孤儿数据。有些课设源码删了外键跑着省事但批改老师只要问一句“你保证成绩和学生一致性靠什么”答不上来就是扣分点。3.2 用户表与登录权限为什么不能把角色写在成绩表里登录是每个课设系统都要有的功能但很多初学方案会把用户名和密码直接塞进学生表里。这样设计的问题在于如果以后要加一个教师或管理员账号就得在学生表里造假学生记录逻辑上很别扭。规范做法是单独的t_user表与业务表解耦。CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 加密后的密码, role VARCHAR(20) DEFAULT STUDENT COMMENT 角色ADMIN / TEACHER / STUDENT, ref_id INT COMMENT 关联学生或教师表的ID, created_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;很多课设源码里密码是明文存着的这在大作业阶段能跑通但如果你想把它写进简历就必须至少做一层SHA-256或MD5加盐处理否则面试官看到明文密码多半会摇头。登录校验的逻辑一般是这样前端表单提交用户名密码到LoginServletServlet里走一次JDBC查询查不到返回“用户名或密码错误”查到了就把用户ID和角色写进session再根据角色跳转到不同的首页。String sql SELECT * FROM t_user WHERE username ? AND password ?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); // 实际项目中这里应该是加密后的密文比对 ResultSet rs ps.executeQuery(); if (rs.next()) { session.setAttribute(uid, rs.getInt(id)); session.setAttribute(role, rs.getString(role)); if (ADMIN.equals(rs.getString(role))) { response.sendRedirect(admin/index.jsp); } else { response.sendRedirect(student/index.jsp); } } else { request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); }注意这里SQL用的是?占位符通过PreparedStatement传入参数。这是防SQL注入的基础操作用户输入的单引号在预编译阶段不会拼进SQL字符串。如果源码里用的是字符串拼接WHERE username name 建议立刻改成占位符不仅是安全问题也是答辩时老师最爱问的技术点——答出“预编译”三个字就能拿分。4. 核心功能链路JDBC连接池、分页查询与成绩导出Excel4.1 用Druid连接池替代DriverManager直连性能和答辩都更好很多课设代码的JDBC写法是每次请求都Class.forName(com.mysql.cj.jdbc.Driver)再DriverManager.getConnection(),俗称“裸连”。这在10个并发以下的演示环境看不出问题但有一个致命短板每个访问都要走TCP握手和MySQL认证三次握手高并发时连接会直接耗尽。更重要的是答辩时主动提一句“我用了连接池”老师基本默认你课下做过功课。推荐用阿里的Druid配置简单且自带监控页面。在src/main/resources下新建druid.propertiesdriverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/score_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4 usernameroot password你的密码 initialSize5 maxActive10 maxWait3000核心参数可以这样理解initialSize是启动时建立多少个连接太小则首次请求慢maxActive是池子里最多多少个连接超过就要排队课设环境设10就足够maxWait是拿不到连接时最多等多久3000毫秒是一个合理的阈值超过直接抛异常避免界面无响应。获取连接的工具类代码public class DbUtils { private static DruidDataSource dataSource; static { try { Properties prop new Properties(); prop.load(DbUtils.class.getClassLoader() .getResourceAsStream(druid.properties)); dataSource (DruidDataSource) DruidDataSourceFactory .createDataSource(prop); } catch (Exception e) { throw new RuntimeException(初始化连接池失败, e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void close(ResultSet rs, Statement stmt, Connection conn) { // 连接池的连接close不是真正关闭而是归还到池中 if (rs ! null) { try { rs.close(); } catch (SQLException ignored) {} } if (stmt ! null) { try { stmt.close(); } catch (SQLException ignored) {} } if (conn ! null) { try { conn.close(); } catch (SQLException ignored) {} } } }这里有一个很多新手容易忽略的细节连接池的close()并不是真正断开数据库连接而是把连接标记为空闲并归还池中。所以finally块里依然要写conn.close()不然连接会被池子里的空闲连接占满最后报wait millis超时。4.2 分页查询与条件筛选PageBean到底在分什么学生成绩列表一旦超过50条全表展示就会又臭又长。课设的标准做法是做一个PageBean把当前页、每页条数、总记录数封装起来减少重复代码。分页的SQL核心是LIMIT子句SELECT s.sno, s.sname, c.cname, sc.score, sc.exam_date FROM t_score sc JOIN t_student s ON sc.student_id s.id JOIN t_course c ON sc.course_id c.id WHERE 1 1 AND s.sname LIKE %张% ORDER BY sc.exam_date DESC LIMIT 0, 10;代码里拼SQL时注意LIMIT的两个参数含义第一个是偏移量offset等于(currentPage - 1) * pageSize第二个是每页条数pageSize。如果页面是第1页、每页10条那偏移是0取第1到10条第2页偏移是10取第11到20条。这个公式几乎是所有分页翻车的第一原因很多人算成currentPage * pageSize导致永远少了第一页的数据。对应的Web层代码int currentPage Integer.parseInt(request.getParameter(page) ! null ? request.getParameter(page) : 1); int pageSize 10; int offset (currentPage - 1) * pageSize; ListScoreVO list new ArrayList(); String sql SELECT ... LIMIT ?, ?; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, offset); ps.setInt(2, pageSize); ResultSet rs ps.executeQuery(); while (rs.next()) { ScoreVO vo new ScoreVO(); vo.setSname(rs.getString(sname)); vo.setScore(rs.getBigDecimal(score)); list.add(vo); } // 再执行一条 SELECT COUNT(*) 单独查出总数这里省略 request.setAttribute(list, list); request.setAttribute(totalPage, totalPage); request.getRequestDispatcher(score_list.jsp).forward(request, response);这里有个容易被忽略的性能小问题COUNT(*)每次分页都要执行数据量不大还好但如果成绩表有几十万条每次都全表扫描就很慢。课程设计阶段不用优化但答辩时你可以主动说“如果数据量大可以把总记录数缓存到应用启动时加载”一句话就能拉开差距。4.3 用POI把成绩单导出成Excel课设演示最加分的功能导出Excel是很多课设系统“隐藏的加分项”因为不是必做功能但做了就能在演示时多讲一分钟。常见做法是使用Apache POI写一个ExportServlet生成.xlsx文件并把响应头设置成附件下载。String fileName 成绩单_ System.currentTimeMillis() .xlsx; XSSFWorkbook workbook new XSSFWorkbook(); XSSFSheet sheet workbook.createSheet(成绩列表); // 创建表头 String[] headers {学号, 姓名, 课程, 成绩, 考试日期}; XSSFRow headerRow sheet.createRow(0); for (int i 0; i headers.length; i) { headerRow.createCell(i).setCellValue(headers[i]); } // 查询数据库逐行写入这里省略JDBC部分 ListScoreVO list getScoreList(); for (int i 0; i list.size(); i) { XSSFRow row sheet.createRow(i 1); row.createCell(0).setCellValue(list.get(i).getSno()); row.createCell(1).setCellValue(list.get(i).getSname()); row.createCell(2).setCellValue(list.get(i).getCname()); row.createCell(3).setCellValue(list.get(i).getScore().doubleValue()); // 设置单元格格式为日期这里简化为字符串 row.createCell(4).setCellValue(list.get(i).getExamDate().toString()); } response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment;filename URLEncoder.encode(fileName, UTF-8)); workbook.write(response.getOutputStream()); workbook.close();代码里有两点要注意文件名里带中文时必须用URLEncoder.encode做一次URL编码否则浏览器下载时大概率乱码或提示“文件不存在”。另外workbook.close()要放在写完流的最后因为POI在写入response流时内部会锁定工作簿太早close会抛异常。这个功能在课设现场演示时直接打开Excel给老师看比在网页上反复点翻页印象深得多。5. 避坑指南这套源码从解压到演示最容易翻车的五个问题5.1 启动Tomcat后访问一直报500ClassNotFound指向JDBC驱动现象控制台报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver或者页面显示长时间处于加载状态后白屏。原因最常见的有两种。一是项目的lib没有被部署到WEB-INF/lib目录下IDEA里Artifact配置没把依赖打进去二是MySQL驱动包版本和数据库版本不匹配比如数据库是MySQL 8.0驱动却是5.1.x的老jar。解决先检查Project Structure - Artifacts - WEB-INF/lib是否有 mysql-connector-java 的jar。没有就右键Put into Output Root或手动加上。如果lib里有驱动但版本老去Maven中央仓库下载最新8.0.x驱动替换。注意不要把jar同时放在Tomcat的lib目录和项目的WEB-INF/lib里那是另一个深坑会造成类加载冲突。我一般会用解压工具看jar包里的MANIFEST.MF确认版本而不是只看文件名。5.2 反复提示“Access denied for user”现象连接数据库时报Access denied for user rootlocalhost。原因多数不是密码错而是当前MySQL客户端不允许root远程访问或者你连接的是localhost但MySQL用户表中的Host列不是localhost。课设源码里常见的连接串是jdbc:mysql://localhost:3306/score_db如果MySQL里root的Host是127.0.0.1或%就可能出现这种边界情况。解决第一步先用命令行本地登录mysql -u root -p能登录说明密码没问题第二步执行SELECT user, host FROM mysql.user WHERE user root;如果Host不是localhost可以在连接串里把localhost改成对应值或者干脆给MySQL加上允许root从任意host访问的权限。但这不是最稳妥的方案更推荐新建一个专用账号只对score_db授权CREATE USER score_userlocalhost IDENTIFIED BY 123456; GRANT ALL PRIVILEGES ON score_db.* TO score_userlocalhost; FLUSH PRIVILEGES;然后把druid.properties里的账号替换成这个专用账号。跑课设没必要用root之前出问题的大多是root密码太复杂或者权限被改过。5.3 页面所有中文全是问号连数据库表里也是现象登录成功进主页学生姓名显示成??或者在浏览器看到鏉庢槑这种乱码。原因三个位置只要有一个字符集不对就会乱。浏览器请求编码、Tomcat响应编码、数据库连接串编码任何一个没对齐结果都是乱码。最常见的翻车位置是JSP页面顶部没有加pageEncoding或者连接串漏了characterEncodingutf8mb4。解决JSP页面第一行写全% page contentTypetext/html;charsetUTF-8 languagejava %连接串里加参数urljdbc:mysql://localhost:3306/score_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4最后在web.xml里加一个编码过滤器这是治本的做法。如果只改上面两步POST请求的中文还是可能乱因为Tomcat默认按ISO-8859-1解码请求体。Filter的写法我在最后一章会贴出来。5.4 成绩删除后id自增计数没重置现象把成绩表清空后再插入一条新成绩id不是从1开始而是接着之前的数字继续加。原因很多人执行的是DELETE FROM t_score;。DELETE不会重置AUTO_INCREMENT计数器只有TRUNCATE才会把表彻底清空并重置自增。这在课设演示时很有意思——老师让你演示“删除所有成绩再录新数据”如果id从7、8继续跳会显得数据不干净。解决在删除功能中如果是管理员清空全部成绩SQL换成TRUNCATE TABLE t_score;注意TRUNCATE不能用在有外键引用且关联表有数据的场景会报错。所以百试百灵的做法是先处理子表成绩再处理主表。如果你只想删除某一条记录用DELETE按主键删是正常的不必担心。这个知识点也经常是答辩题的隐藏考点。5.5 Tomcat端口被占用启动后立刻报Failed to initialize end point现象IDEA控制台报Port 8080 is already in use或java.net.BindException: Address already in use。原因要么之前启动的Tomcat没有关闭干净要么有别的程序占了8080端口。Windows下还常见是因为上次IDEA非正常关闭残留了java进程。解决在终端查找占用端口的进程netstat -ano | findstr 8080记下最后一列的PID然后强制结束taskkill /PID 你的PID /F如果每次都是8080被占可以改Tomcat端口在conf/server.xml里把Connector port8080改成8081同时把IDEA的HTTP端口同步改成8081。我不建议为了图省事经常改端口因为端口一换JSP页面里写死的重定向地址也要跟着改有事没事给自己找麻烦。最好还是找到那个残留进程治标治本。6. 把98分的课设改造成简历项目的三个加分技巧6.1 用Filter统一解决编码和登录拦截一句话讲清楚拦截器这是我能给的最划算的改造。新建一个EncodingFilter实现javax.servlet.Filter接口public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); chain.doFilter(request, response); } }在web.xml中注册注意url-pattern写/*覆盖所有路径filter filter-nameencodingFilter/filter-name filter-classcom.yourname.filter.EncodingFilter/filter-class /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping登录拦截同理写一个LoginFilter在doFilter里检查session有没有uid没有就重定向到login.jsp。你可以在简历的项目描述里写一句“使用Filter完成统一编码和登录鉴权避免每个Servlet重复判断登录状态”这一句话比写十行代码更能体现工程意识。6.2 用分组聚合SQL代替Java内存循环面试官最想听到的优化点课设里有个常见功能查看某个学生的总成绩、平均分、加权学分绩。很多代码是把成绩查出来在Java里用for循环累加。更好的做法是把计算下推到SQL里SELECT s.sname, ROUND(AVG(sc.score), 2) AS avg_score, SUM(c.credit * sc.score) / SUM(c.credit) AS weighted_score FROM t_score sc JOIN t_student s ON sc.student_id s.id JOIN t_course c ON sc.course_id c.id WHERE sc.student_id ? GROUP BY s.sname;如果成绩表中有一门课是0分或缺考NULLAVG会自动忽略NULL值但SUM如果遇到全NULL会返回NULL所以要用IFNULL(SUM(...), 0)包一层。答辩时你说“聚合计算交给数据库而不是Java循环数据量大时有数量级的性能差距”老师基本不会继续追问但会记下你有优化的意识。6.3 给密码加盐哈希替换裸存的明文密码如果课设源码的用户表里存的是明文密码比如123456趁早改成加盐哈希存储。不需要引入Spring Security这种超大框架用JDK自带的MessageDigest就能实现public static String saltPassword(String password, String salt) { String src salt password; byte[] bytes src.getBytes(StandardCharsets.UTF_8); byte[] digest null; try { digest MessageDigest.getInstance(SHA-256).digest(bytes); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); }用户在注册时生成一个随机salt比如UUID的前8位String salt UUID.randomUUID().toString().substring(0, 8);把salt和哈希后的密码一起存到用户表加一列salt。登录时先用salt加上输入的密码算哈希再与库里存储的密文比对。这样做的好处是即使数据库泄露攻击者拿到的也是密文想反推原始密码需要大量彩虹表计算成本高得多。这三件事我每次给学弟学妹改课设都会顺手做掉编码过滤器十分钟搞定把SQL聚合计算改成 GROUP BY 二十分钟密码加盐哈希半小时。改完之后这段代码从“能跑的课设”变成了“能讲的工程”。如果你也手头正好有一份成绩管理系统的源码我建议先跑起来再按照这三步逐项加固每一步都能在答辩演示时边说边打开代码给老师看。希望帮到你。本文还有配套的精品资源点击获取