多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

JSP+SQL Server登录注册实战:从环境配置到安全加固

JSP+SQL Server登录注册实战:从环境配置到安全加固 简介面向初学者的 JSPSQL Server 登录注册项目包解决 Web 开发中常见的用户身份验证需求。项目覆盖数据库表设计、JDBC 连接、表单提交处理到 HttpSession 会话管理的完整链路并包含密码加密存储、防 SQL 注入等安全措施适合 Java Web 新手对照练习与二次开发。压缩包共 46 个文件其中 28 个 JSP 页面为主要代码载体另有配置文件、Java 类及工程元数据等支撑内容整体仅 581KB结构轻量便于在 Eclipse 或 IntelliJ IDEA 中快速部署调试。已有 835 人学习下载是理解 JSP 与 SQL Server 集成的实用入门实例。通过该实例可掌握注册登录完整实现包括表单校验、用户入库、身份验证及会话保持等关键环节为后续开发更复杂的企业级应用打好基础。1. 登录注册不难值钱的是“能跑又能改”JSP SQL Server 登录注册这个组合听上去是大学课程设计的标配但正因为是标配才最能看出一个开发者的基本功。这个压缩包里装的东西大多是三类内容JSP页面、Servlet类、SQL脚本外加一个需要你自己配置的数据库环境。它的价值不在于代码有多惊艳而在于它把“浏览器提交表单→服务器校验→数据库读写→Session维持登录态”这一整条链路完整走通了。你能把它跑起来就摸清了Java Web开发最核心的那条主脉。这套东西适合谁用最简单的一类是课程设计、毕业设计需要快速交付的同学第二种是刚学完Java语法、想看看真实Web项目长什么样的新手第三种是想拿现成代码改造成自己业务的在职工程师。但坦白说下载这个ZIP只是第一步真正让你卡住三天三夜的永远是SQL Server连不上、驱动加载失败、端口被占用这些破事。所以这篇文章我按“先跑通、再改代码、再避坑”的顺序讲最后给你一套值钱的改造思路——让这个“课设项目”变成面试时能聊十分钟的作品。先说清楚这个项目怎么把环境配起来。2. 把 JSP SQL Server 环境跑通JDK、Tomcat、驱动一个都不能少2.1 JDK 和 Tomcat 版本怎么配对才算稳拿到ZIP先别急着解压先检查你的环境三件套JDK版本、Tomcat版本、SQL Server版本。这三者之间是强绑定关系配错一个就是无休止的报错。最常见的稳定组合是JDK 8 Tomcat 8.5 或 9.0 SQL Server 2012~2019这个组合下几乎不会有版本兼容的幺蛾子。为什么强调JDK 8因为这个项目里如果是老代码通常用的是 javax.servlet 包JDK 8 完全兼容。但如果你装了 JDK 11 以上Tomcat 用新版倒也没事可一旦碰到旧代码里某些过时的写法就会冒出一堆莫名其妙的问题。Tomcat 10 以上更麻烦它把 javax.servlet 换成了 jakarta.servlet你手里这个ZIP里的代码如果import的是javax直接编译不过。所以我的习惯是不折腾版本直接JDK 8 Tomcat 8.5。安装完Tomcat后先把环境变量配好# Windows 示例Linux/macOS 同理 JAVA_HOMEC:\Program Files\Java\jdk1.8.0_202 CATALINA_HOMED:\apache-tomcat-8.5.100 Path 追加: %JAVA_HOME%\bin;%CATALINA_HOME%\bin验证环境是否就绪java -version catalina version注意catalina version这个命令在Tomcat的bin目录下执行。如果两条命令都正常输出版本号说明基础环境没问题。接下来要处理的是这个项目最难缠的部分——SQL Server 驱动。2.2 SQL Server 驱动和连接字符串版本配错就是黑匣子JSP页面本身不直接连数据库它要通过JDBC驱动跟SQL Server对话。这个ZIP里大概率已经带了一个sqljdbc4.jar或sqljdbc42.jar但你的驱动版本必须和JDK版本匹配。判断方法很简单sqljdbc4.jar对应JDK 6/7sqljdbc42.jar对应JDK 8。如果你用JDK 8却拿了个sqljdbc4跑起来会报UnsupportedClassVersionError这属于最容易翻车的版本坑。驱动的存放位置也很讲究标准做法是把驱动jar包复制到WEB-INF/lib/目录下而不是放在Tomcat的lib目录。原因在于项目部署时Tomcat会加载每个应用WEB-INF/lib下的jar如果把驱动放到全局lib你换了项目环境就忘了带驱动属于给自己埋雷。连接字符串这块SQL Server 和 MySQL 写法完全不同很多从MySQL转过来的朋友会在这卡住。SQL Server 的标准写法长这样// 数据库连接配置 - DBUtil.java private static final String DRIVER com.microsoft.sqlserver.jdbc.SQLServerDriver; private static final String URL jdbc:sqlserver://localhost:1433;databaseNameuser_db;encryptfalse;trustServerCertificatetrue; private static final String USERNAME sa; private static final String PASSWORD your_password; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } }这里重点讲两个参数。encryptfalse是SQL Server 2019及以上版本必须加的因为新版驱动默认启用加密连接而本地开发通常没配证书不加这个会报SSL错误。trustServerCertificatetrue表示信任自签名证书本地开发专用。生产环境不建议这么干但课程设计完全够用。登录名默认用sa账户但你得在SQL Server里先给sa设好密码并启用混合认证模式要不然连接会直接被拒。这一步操作步骤后面第5章会细说。2.3 先建库再建表用户表字段设计的五种常见取舍环境配好了现在该把SQL脚本跑起来。一般来说ZIP里会带一个userdb.sql或db.sql文件不要直接双击运行先打开看一遍内容理解表结构才是重点。典型的用户表结构长这样-- 用户表支持登录注册的最小字段集合 CREATE TABLE t_user ( id INT IDENTITY(1,1) PRIMARY KEY, username NVARCHAR(50) NOT NULL UNIQUE, password NVARCHAR(64) NOT NULL, nickname NVARCHAR(50), email NVARCHAR(100), create_time DATETIME DEFAULT GETDATE() );这里有个字段类型的设计门道用户名一定要用NVARCHAR不要用VARCHAR。因为SQL Server里VARCHAR存的是非Unicode字符中文会乱码NVARCHAR存Unicode中文、表情都能正常存储。这个点也是搜索热词里“sqlserver 字符串转数字”的连带问题——如果你在SQL语句里把字符串字段和数字类型比较SQL Server会做隐式类型转换轻则查询变慢重则直接报错。password NVARCHAR(64)存的是哈希后的密码不是明文。这就是个值得改造的点很多ZIP里的原版代码直接存明文密码你拿到手以后最好改成MD5加盐这章后面细讲。先建库-- 建库脚本 CREATE DATABASE user_db; GO USE user_db; GO -- 再把上面的建表语句跑一遍跑SQL脚本的方式有两种一种是在SQL Server Management Studio里逐段执行另一种用命令行。命令行适合没装图形界面工具的情况sqlcmd -S localhost -U sa -P your_password -i userdb.sql注意sqlcmd是SQL Server自带的命令行工具。执行完以后用下面的命令确认表真的建出来了SELECT name FROM sys.tables;2.4 数据库连接不用连发给JDBC连接加个池子原始的JDBC写法是每次请求都DriverManager.getConnection()用完再关。这种方式在登录注册这种低频交互下问题不大但一旦并发上来每次新建连接的时间都要几百毫秒用户的表现就是页面转圈。这就是为什么稍微像样点的JSP项目要用连接池。常见的做法是引入Apache Commons DBCP 或 C3P0这里以DBCP为例改动很小但收益巨大。首先下载commons-dbcp2.jar、commons-pool2.jar、commons-logging.jar放进WEB-INF/lib然后写一个连接池工具类// 连接池工具类 - DBCPUtil.java import org.apache.commons.dbcp2.BasicDataSource; import javax.sql.DataSource; public class DBCPUtil { private static BasicDataSource dataSource; static { dataSource new BasicDataSource(); dataSource.setDriverClassName(com.microsoft.sqlserver.jdbc.SQLServerDriver); dataSource.setUrl(jdbc:sqlserver://localhost:1433;databaseNameuser_db;encryptfalse;trustServerCertificatetrue); dataSource.setUsername(sa); dataSource.setPassword(your_password); dataSource.setInitialSize(5); // 初始连接数 dataSource.setMaxTotal(20); // 最大连接数 dataSource.setMaxIdle(10); // 最大空闲连接 dataSource.setMinIdle(3); // 最小空闲连接 dataSource.setMaxWaitMillis(3000); // 获取连接超时时间 } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }逻辑说明BasicDataSource是DBCP的核心类几个参数决定了连接池行为。initialSize5表示应用启动时就预创建5个连接避免第一个用户进来时现场建连。maxTotal20是硬上限SQL Server默认最大并发连接数通常够用别贪大。maxWaitMillis3000是获取连接的等待时间超过3秒直接报超时避免线程全卡在获取连接上。这个改造的收益是登录注册从“每次请求都握手”变成“从池子里拿现成连接”响应时间能下降一个数量级。这也是面试官最常追问的点“你做过数据库连接池优化吗”现在你有真实经验了。3. 登录注册核心链路从表单到 Session 的每一步3.1 注册页和登录页的表单字段到底要留几个很多网上下载的登录注册页面表单字段堆得密密麻麻用户名、密码、确认密码、邮箱、手机号、验证码、性别、生日……这看起来功能丰富实际维护起来是灾难。理想的注册页只留四个字段用户名、密码、确认密码、验证码。邮箱和手机号可以放到注册成功后的“完善资料”页面去不要在注册这一步逼用户填一堆东西。登录页更简单用户名、密码、验证码。记住密码用复选框实现别整指纹、短信验证那套花活。表单的HTML代码一般长这样!-- register.jsp -- form action${pageContext.request.contextPath}/register methodpost label用户名/label input typetext nameusername maxlength20 required label密码/label input typepassword namepassword maxlength20 required label确认密码/label input typepassword nameconfirmPassword maxlength20 required span classerror${errorMsg}/span button typesubmit注册/button /form这里有个细节${pageContext.request.contextPath}是EL表达式作用是自动适配项目路径。如果你在表单action里写死/register项目部署时上下文路径一改表单提交就404了。用EL表达式就永远不用改。还有个常见经验表单前端要做校验但后端必须再做一次校验。前端校验是给用户看的提示友好后端校验才是真正防攻击的。前端可以绕过直接用Postman往你的Servlet发请求。3.2 Servlet 里验证参数空值、格式和 sqlserver 字符串转数字的坑表单提交过来后Servlet 是第一道防线。这部分的代码逻辑要拆成四步拿参数、验空、验格式、查重。很多ZIP里的原版代码根本没做这四步导致用户直接提交空表单也能过。最典型的低级错误就是拿参数后直接执行SQL这样空字符串也能插进数据库// RegisterServlet.java - 核心逻辑 WebServlet(/register) public class RegisterServlet extends HttpServlet { private UserDao userDao new UserDao(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 处理中文乱码 req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); // 2. 拿参数并做基础校验 String username req.getParameter(username); String password req.getParameter(password); String confirmPwd req.getParameter(confirmPassword); if (username null || username.trim().isEmpty() || username.length() 20) { req.setAttribute(errorMsg, 用户名不能为空且长度不超过20); req.getRequestDispatcher(/register.jsp).forward(req, resp); return; } if (password null || password.length() 6) { req.setAttribute(errorMsg, 密码至少6位); req.getRequestDispatcher(/register.jsp).forward(req, resp); return; } if (!password.equals(confirmPwd)) { req.setAttribute(errorMsg, 两次密码不一致); req.getRequestDispatcher(/register.jsp).forward(req, resp); return; } // 3. 查重 插入 User user new User(username, MD5Util.md5(password)); boolean success userDao.register(user); if (success) { resp.sendRedirect(login.jsp); } else { req.setAttribute(errorMsg, 用户名已存在); req.getRequestDispatcher(/register.jsp).forward(req, resp); } } }这里有个新手常踩的坑前端已经用required属性做了非空校验为什么后端还要再写一遍。原因很简单——required只是浏览器行为用户可以直接禁用JavaScript或者用工具构造请求。如果你只靠前端校验一条伪造的POST请求就能把空字符串写进数据库数据库里全是脏数据。关于“sqlserver 字符串转数字”这个坑往往出在用户名是纯数字的情况。比如用户名 “12345”SQL Server 在比较时如果字段类型是NVARCHAR、参数是字符串还好但如果你写SQL时用了WHERE username 12345没加引号SQL Server 会把字段隐式转成数字再比这样索引失效都是轻的一旦数据量大点全表扫描能把数据库拖死。所以DAO层写SQL时必须用参数化查询后面第4章会细讲。3.3 密码绝不能明文存MD5 加盐的三种写法这是第二个值得改造的典型点。很多ZIP里的原版代码注册时password request.getParameter(password)然后直接存库登录时也直接比对明文。这种代码在你自己的课设里跑没问题但如果你拿这套去面试面试官一句“密码明文存储被人拖库怎么办”就能把你问倒。至少要做一层MD5加盐。加盐的意思是在原始密码后面拼一串随机字符再做哈希避免两个用户相同密码得到相同哈希值。三种常见写法// 写法一固定盐最简单但价值有限 public static String md5(String password) { String salted my_fixed_salt_ password; return DigestUtils.md5Hex(salted); } // 写法二随机盐盐存数据库推荐 public static String md5WithSalt(String password, String salt) { return DigestUtils.md5Hex(salt password); } // 写法三加盐多次迭代SHA-256 更安全 public static String sha256WithSalt(String password, String salt) { MessageDigest md MessageDigest.getInstance(SHA-256); byte[] hash md.digest((salt password).getBytes(StandardCharsets.UTF_8)); return HexFormat.of().formatHex(hash); }说明写法一适合快速改造但固定盐一旦泄露所有密码都完蛋。写法二是最常见的方案UUID.randomUUID().toString().substring(0, 8)生成随机盐存到用户表的salt字段里校验时取出盐再拼上输入的密码哈希比对。写法三安全性更高但需要额外引入commons-codec等工具库。注册时生成盐、存哈希登录时取盐、重算哈希、比对// 注册时 String salt UUID.randomUUID().toString().substring(0, 8); User user new User(username, sha256WithSalt(password, salt), salt); // 登录时 User dbUser userDao.findByUsername(username); String inputHash sha256WithSalt(inputPassword, dbUser.getSalt()); if (inputHash.equals(dbUser.getPassword())) { // 登录成功 }这样做的价值在于即使数据库泄露攻击者拿到的是一串无意义的哈希短盐还好随机盐就得穷举成本高到他不值得。3.4 session 与 Cookie登录态维持的两种机制登录注册功能的核心不是“登录成功的那一刻”而是“登录之后怎么让服务器记住你”。这里涉及两个概念Session服务端状态和 Cookie客户端状态。登录成功后把用户信息放进Session// LoginServlet.java 登录成功分支 HttpSession session req.getSession(); session.setAttribute(userId, user.getId()); session.setAttribute(username, user.getUsername()); session.setMaxInactiveInterval(30 * 60); // 30分钟超时 resp.sendRedirect(index.jsp);然后在需要登录才能访问的页面用一段简单的Filter做拦截// 登录拦截器 - LoginFilter.java WebFilter(/*) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); String uri request.getRequestURI(); boolean isStatic uri.endsWith(.css) || uri.endsWith(.js) || uri.endsWith(.jpg); boolean isAuthPage uri.endsWith(login.jsp) || uri.endsWith(register.jsp); boolean isAuthAction uri.endsWith(/login) || uri.endsWith(/register); if (session ! null session.getAttribute(userId) ! null) { chain.doFilter(req, resp); // 已登录放行 } else if (isStatic || isAuthPage || isAuthAction) { chain.doFilter(req, resp); // 公开资源放行 } else { response.sendRedirect(login.jsp); } } }这个Filter是登录注册项目里最容易忽略的部分。很多ZIP里的代码登录成功后跳转index.jsp但直接在浏览器里输入index.jsp的URL也能访问那就等于什么保护都没有。“记住我”的实现则依赖Cookie。勾选记住我之后在登录接口里写一个长期CookieCookie cookie new Cookie(rememberMe, UUID.randomUUID().toString()); cookie.setMaxAge(7 * 24 * 60 * 60); // 7天有效 response.addCookie(cookie);Cookie的值设计上不应该直接是用户名或密码而应该是一个随机Token服务端维护一个“Token→用户ID”的映射表。这样即使Cookie泄露别人也只是拿到一个临时凭证。记住一个原则Session存的是会话态Cookie存的是凭证令牌明文信息不要进Cookie。4. 让项目经得起拷问安全与并发的基础代价4.1 PreparedStatement 为什么必须取代 Statement这是老生常谈但也是面试最常问的。ZIP里如果有DAO层代码八成里面是Statement拼SQL// 错误示范字符串拼接SQLSQL注入门户大开 String sql SELECT * FROM t_user WHERE username username AND password password ; Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql);如果用户输入的username是 or 11拼出来的SQL就变成SELECT * FROM t_user WHERE username or 11 AND password ...这等于裸奔。正确写法是PreparedStatement// UserDao.java - 登录查询 public User findByUsernameAndPassword(String username, String password) { String sql SELECT * FROM t_user WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { User user new User(); user.setId(rs.getInt(id)); user.setUsername(rs.getString(username)); return user; } } } catch (SQLException e) { e.printStackTrace(); } return null; }逻辑说明PreparedStatement的核心价值是预编译与参数分离。?占位符意味着输入值只作为数据处理永远不会被拼接成SQL指令的一部分。哪怕输入 or 11它也只是作为一个字符串跟数据库里的字段值做比较。性能上还有额外收益SQL结构预编译一次多次执行时数据库不需要重新解析高并发场景效率也更高。4.2 验证码与登录失败次数限制防爆破的两个实用段位登录注册功能成形后总有人想用脚本暴力撞库。防暴力破解的最低成本方案是图形验证码ZIP里通常自带一个CaptchaServlet但很多版本实现得很简陋——验证码只有数字没有干扰线OCR一识别就能过。我的建议是直接改成计算型验证码比如“3 5 ”这比图形验证码更抗识别// 验证码生成逻辑简化版 int result num1 num2; // 把result存进Session session.setAttribute(captchaResult, result); // 页面显示 3 5 拿到用户提交的答案后比对Session里存的真实结果。这种方式不需要引入第三方库对新手最友好。但验证码挡不住所有脚本。再进一步做登录失败次数限制。常见的做法是用Redis或一张表记录每个用户名/IP的失败次数5次失败后锁定15分钟。// 登录失败的计数逻辑简化版用HashMap模拟 private static MapString, Integer failCountMap new ConcurrentHashMap(); // 登录失败时 String key login_fail_ username; failCountMap.put(key, failCountMap.getOrDefault(key, 0) 1); // 超过5次锁定 if (failCountMap.get(key) 5) { // 记录锁定时间并拒绝登录 }说明一下这个方案用HashMap是刻意简化的应用重启计数就清了但因为登录注册的量级很小这并不影响功能验证。如果你想让计数持久化把它写进数据库一张login_attempt表里即可。面试时提到这点说明你考虑过暴力破解的风险。4.3 简单防刷注册接口频控的低成本实现验证码防的是登录爆破注册接口也要防刷注册。常见做法是限制同一IP的注册频率// 注册频控的简化实现 private static final MapString, Long registerTimes new ConcurrentHashMap(); // RegisterServlet 里 String ip request.getRemoteAddr(); long now System.currentTimeMillis(); Long lastTime registerTimes.get(ip); if (lastTime ! null now - lastTime 60 * 1000) { // 同一IP 60秒内只能注册一次 req.setAttribute(errorMsg, 注册过于频繁请稍后再试); req.getRequestDispatcher(/register.jsp).forward(req, resp); return; } registerTimes.put(ip, now);这种做法的问题是IP可能被伪造或共用比如公司出口IP所有人都是同一个。但用在课设或个人项目里它已经能挡住绝大多数恶意脚本了。进一步的做法是把计数存到数据库加一个时间窗口。这是你能写在简历里的东西做过注册接口的防刷限流设计。这里有段关于成本的话值得说安全和性能都是经营出来的。你不需要在课程设计里搞出这种多因子认证但你知道这些机制的存在并能解释实现路径面试就已经赢了大多数同学。5. 避坑JSP SQL Server 登录注册项目最常见的 5 个翻车现场5.1 Tomcat 启动成功但访问 login.jsp 404现象Tomcat 正常启动打开http://localhost:8080/login.jsp却提示404。原因没部署项目导致的路径不对。Tomcat 的默认根路径是http://localhost:8080/但你是想把webapps下某个文件夹或ZIP解压出来的war包作为应用。如果项目叫login-demo正确的访问路径应该是http://localhost:8080/login-demo/login.jsp。解决先把ZIP解压或war包放入Tomcat的webapps目录重新启动Tomcat后访问http://localhost:8080/项目名/login.jsp。如果你不想用项目名把目录改成ROOT替换掉默认的就能直接用根路径访问。# 项目部署的标准流程 cd %CATALINA_HOME%\webapps mkdir login-demo # 把ZIP内容复制到 login-demo 目录 # 然后重启 Tomcat5.2 注册时中文用户名存入数据库变成???现象数据库里username字段存的是“???”或乱码英文正常。原因两个层面的问题。一是数据库字段类型用了VARCHAR而不是NVARCHAR非Unicode类型存不了中文。二是JDBC连接字符串里没指定characterEncoding相关配置驱动默认字符集不对。解决首先把表字段改成NVARCHAR(50)其次在建表时给表指定中文排序规则。如果是存量表用这条SQL改ALTER TABLE t_user ALTER COLUMN username NVARCHAR(50) COLLATE Chinese_PRC_CI_AS;然后改JDBC连接字符串SQL Server的驱动不是用characterEncodingutf-8那套而是直接在URL里加jdbc:sqlserver://localhost:1433;databaseNameuser_db;encryptfalse;characterEncodingUTF-8;useUnicodetrue最后Servlet 里记得加上request.setCharacterEncoding(UTF-8)。这三步做完中文基本上不会再乱码。5.3 登录报错无法打开登录所请求的数据库现象浏览器访问注册页面时提示Cannot open database user_db requested by the login。原因SQL Server里没有user_db这个数据库或者数据库文件存在但没附加到实例。解决先用sqlcmd命令确认sqlcmd -S localhost -U sa -P your_password -Q SELECT name FROM sys.databases如果没有这个库把SQL脚本重新跑一遍。如果你拿到的是.mdf文件用附加方式挂载CREATE DATABASE user_db ON (FILENAME ND:\data\user_db.mdf) FOR ATTACH;附加完以后还要确认登录用户sa对这个库有权限USE user_db; GO EXEC sp_changedbowner sa; GO5.4 登录成功后跳转回登录页状态总是丢现象用户名密码正确明明跳转到了index.jsp刷新一下又回到login.jsp。原因Filter的拦截逻辑有问题。跳转到index.jsp后浏览器再发请求时Filter检查Session发现session.getAttribute(userId)为null。说明登录成功时Session里根本没存进去东西或者Session在跳转前被销毁了。解决检查LoginServlet最常见的错误是登录失败分支里调用session.invalidate()时把成功分支的session也一起废了。另一个高频原因是跳转方式不对——用了req.getRequestDispatcher(index.jsp).forward()但Servlet已经被Filter拦截while循环式跳转。改成response.sendRedirect(index.jsp)即可。5.5 SQL Server 2019 安装后连不上无法找到数据库引擎启动句柄现象注册功能连数据库时报错查服务发现SQL Server数据库引擎服务没起来。事件查看器里提示“无法找到数据库引擎启动句柄”。原因SQL Server安装时服务账户权限不够或者端口配置冲突。另一个常见情况是安装时选了“只装管理工具”没装数据库引擎服务。解决打开SQL Server配置管理器看“SQL Server服务”节点里有没有SQL Server (MSSQLSERVER)这一项。没有就去安装介质里重新选“安装全新独立安装”勾选“数据库引擎服务”。有但启动失败就检查Windows服务账户——右键服务选属性把登录身份改成Local System然后重启服务。如果端口冲突查一下1433端口有没有被其他程序占用netstat -ano | findstr 1433被占用就把SQL Server的端口改掉或者停掉占用进程。这一步做完数据库服务才能真正跑起来。6. 项目改造的进阶顺序把这个课设变成能面试聊的作品如果你手里这套登录注册已经能跑通下一步别急着加花里胡哨的功能按这个顺序改造性价比最高。第一步把密码改为加盐哈希存储。这是安全隐患最大的一环改完以后你可以在简历里写“密码采用加盐哈希存储保障用户数据安全”一句话就能拉高项目档次。第二步用PreparedStatement全面替换Statement。如果你原来的DAO层是拼接SQL这步非做不可。改完后顺手把DBUtil换成DBCPUtil把数据库连接池化并发能力拉高一个数量级。第三步补上日志。在你的Servlet和DAO里加上Logger。别小看这个改动如果上线后出了问题没有日志就跟在乱抓瞎一样煎熬。新手最容易忽略的正是这个“不产生功能价值”的加分项。import org.slf4j.Logger; import org.slf4j.LoggerFactory; // 每个Servlet里加一行 private static final Logger LOGGER LoggerFactory.getLogger(RegisterServlet.class); // 注册失败时记录 LOGGER.warn(用户注册失败用户名: {}, 原因: {}, username, errorMsg);第四步给登录和注册加防刷逻辑。参考第4章的实现登录失败上限注册频控。这个改动量不大但面试官一听就知道你考虑过真实场景。第五步改造前端页面为AJAX提交。原来是表单整页提交改成AJAX后体验会明显变好// register.js - 前端AJAX提交 $(#registerBtn).click(function () { var username $(#username).val(); var password $(#password).val(); var confirmPwd $(#confirmPassword).val(); $.ajax({ url: contextPath /register, type: POST, data: {username: username, password: password, confirmPassword: confirmPwd}, dataType: json, success: function (res) { if (res.code 0) { window.location.href login.jsp; } else { $(#errorMsg).text(res.msg); } } }); });改造完后做什么验证给你一个验证清单第一用正确密码登录第二用错误密码登录5次触发锁定第三注册一个中文用户名再登录确认不乱码第四退出登录后再访问index.jsp确认被拦截跳回login.jsp第五在未登录状态下尝试http://localhost:8080/项目名/admin.jsp如果有确认不能越权访问。最后的经验之谈这套JSP SQL Server登录注册项目你把它跑通只是及格把它改造成上面这样才是真正学会。我自己带过的新人里凡是能把这四步改造做完的入职后看老项目代码基本都不害怕。技术本身不难难的是你愿不愿意把那个“能用就好”的ZIP拆开逐行读一遍然后试着动手改两处。希望帮到你。提示如果你拿到的ZIP连README都没有别慌。先看目录结构Java项目找srcWeb项目找WEB-INF/web.xml那个文件是Web应用的入口说明书。把 web.xml 打开看一眼哪些Servlet映射到哪些路径整个项目的路由就清楚了。本文还有配套的精品资源点击获取
返回列表