版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
JavaWeb开发技术与案例教程第12章Web网站安全第12章知识结构12.1网站安全的重要性12.2案例学习:网站面临的主要威胁•12.2.1SQL注入•12.2.2跨站脚本攻击•12.2.3跨站请求伪造•12.2.4URL操作攻击•12.2.5密码保护与验证12.3验证码功能的应用•12.3.1生成验证码图片•12.3.2验证码校验•12.3.3创建表单12.4本章小结1第12章课程导入
JavaWeb应用已成为企业核心业务的重要载体,而安全则是其生命线的基石。从用户隐私泄露到系统瘫痪,从数据篡改到资金盗取,Web安全威胁正以惊人的速度演变,对开发者提出了前所未有的挑战,因此在开发过程中,需要构建覆盖传输、存储、访问控制的全链路防护体系。本章对JavaWeb网站安全进行详细探讨,涉及SQL注入、XSS攻击、URL操作攻击、密码保护与验证等问题。◆本章要点•理解SQL注入的原理(通过拼接字符串篡改SQL),并掌握使用PreparedStatement作为核心防御手段。•理解XSS攻击原理,并掌握使用JSTL的<c:out>标签或其他方式对输出进行HTML编码来防范。•理解CSRF(跨站请求伪造)攻击的原理(利用用户已登录的会话),并掌握使用Token验证的防御措施。•掌握密码存储的安全实践,即必须使用BCrypt等加盐哈希算法存储密码,禁止明文存储。•掌握验证码的实现原理:后端Servlet生成图片并存入session,前端表单提交后由后端校验。2第12章课程导入◆素养目标•树立网络安全责任意识,践行守护者角色:通过SQL注入、XSS、CSRF等攻击案例分析,理解“开发者是网站安全的第一责任人”。•强化加密技术应用,保护数据机密性:在密码加盐哈希存储实践中,掌握BCrypt等算法的应用。•培养安全编码习惯,从源头消除漏洞:通过输入验证与输出编码的双重防护,树立“安全左移”的开发理念。•弘扬法治精神,遵守网络安全法规:结合《中华人民共和国网络安全法》《中华人民共和国数据安全法》学习,践行合规开发。312.1网站安全的重要性(1)随着信息技术的飞速发展,JavaWeb应用程序已成为企业核心业务与日常数字生活不可或缺的载体。从电子商务、在线银行到社交媒体,这些应用处理和存储着海量的敏感数据,包括用户个人信息、财务记录乃至企业的核心知识产权。因此,Web应用程序的安全性已不再是项目开发中的一个可选项,而是贯穿于整个软件开发生命周期的基石,是决定一个系统能否值得信赖的根本前提。一个安全防线薄弱的网站,如同一个敞开大门的数字金库,随时可能面临来自恶意行为者的各种攻击,如SQL注入、XSS攻击或远程代码执行(RemoteCodeExecution,RCE)等。一旦安全防线被攻破,其后果往往是多维度且极其严重的,主要体现在以下几个方面:412.1网站安全的重要性(2)
业务中断风险许多网络攻击旨在通过耗尽服务器资源来瘫痪服务,导致网站无法访问,业务运营陷入停滞。这种服务中断不仅影响用户体验,更直接冲击企业的生产力和收益,在极端情况下甚至可能导致业务的全面停摆。因此确保网站的安全,是保护用户数据、维护企业声誉、规避法律风险和财务损失的根本保障。对于每一位开发者而言,树立“安全是第一责任人”的意识,将安全思维融入到架构设计、代码编写和测试部署的每一个环节,是专业素养的核心体现。本章将详细探讨网站面临的主要威胁,并介绍相应的防御技术与最佳实践。512.2案例学习:网站面临的主要威胁
JavaWeb网站面临的主要安全威胁包括SQL注入、跨站点脚本、跨站请求伪造、URL操作攻击和密码破解等。本节将举例说明常见的Web攻击方式。612.2.1SQL注入
SQL注入是一种代码注入技术,它允许攻击者通过操纵应用程序的输入字段,将恶意的SQL代码插入到后端数据库查询中,从而绕过应用程序的安全措施,访问、修改或删除数据库中的数据。在JavaWeb应用中,SQL注入是一个常见的安全漏洞,必须得到妥善的防范。◆SQL注入的工作原理假设有一个JavaWeb应用,使用JDBC与数据库进行交互,通过学号查询成绩清单。如果应用中的SQL查询没有正确地处理用户输入,攻击者就可能会插入恶意的SQL代码。实例代码如下所示。首先,需要在数据库university中添加一张名称为score的数据表,创建语句如下所示:CREATETABLEIFNOTEXISTSuniversity.score(student_idVARCHAR(100),mathINT,englishINT);712.2.1SQL注入(代码示例)然后,向score表中插入数据记录,插入语句如下所示:INSERTINTOuniversity.scorevalues('001',66,77);INSERTINTOuniversity.scorevalues('002',77,88);INSERTINTOuniversity.scorevalues('003',88,77);INSERTINTOuniversity.scorevalues('004',88,100);INSERTINTOuniversity.scorevalues('005',66,99);812.2.1SQL注入清单12-1所示的代码用于演示SQL注入漏洞的查询表单页面,通过POST方式将学号参数提交到queryServlet进行处理,由于未对用户输入进行过滤和验证,存在潜在的安全风险。912.2.1SQL注入(清单12-1query_form.jsp)<%@pagecontentType="text/html;charset=UTF-8"language="java"%><html><head><title>SQL注入演示</title></head><body><formmethod="POST"action="./queryServlet"><inputtype="text"name="student_id"value=""></input><inputtype="submit"value="查询"></input></form></body></html>1012.2.1SQL注入清单12-2所示的代码是一个存在严重SQL注入漏洞的查询Servlet,通过直接拼接用户输入的学号参数构建SQL查询语句,使用Statement执行查询并返回学生成绩数据,由于未对用户输入进行任何过滤或参数化处理,攻击者可以通过构造恶意输入来执行任意SQL命令。1112.2.1SQL注入(清单12-2QueryServlet.java)1/3packagecom.example.ch12;importjakarta.servlet.ServletException;importjakarta.servlet.annotation.WebServlet;importjakarta.servlet.http.HttpServlet;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.sql.*;//定义Servlet访问路径为/queryServlet@WebServlet("/queryServlet")publicclassQueryServletextendsHttpServlet{@OverrideprotectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{resp.setCharacterEncoding("UTF-8");resp.setContentType("text/html;charset=UTF-8");try{//1.加载MySQLJDBC驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.配置数据库连接参数Stringurl="jdbc:mysql://:3306/university";StringdbUser="root";//数据库用户名1212.2.1SQL注入(清单12-2QueryServlet.java)2/3StringdbPass="root";//数据库密码//3.获取用户输入参数(注意:此处存在SQL注入风险)Stringid=req.getParameter("student_id");//4.拼接SQL语句(存在风险)Stringquery="SELECTstudent_id,math,englishFROMscoreWHEREstudent_id='"+id+"'";//使用try-with-resources确保Connection和Statement被自动关闭try(Connectionconnection=DriverManager.getConnection(url,dbUser,dbPass);Statementstatement=connection.createStatement()){//5.执行查询并获取结果集(ResultSet也会被自动关闭)try(ResultSetresultSet=statement.executeQuery(query)){//6.处理结果集并输出数据while(resultSet.next()){StringstudentId=resultSet.getString("student_id");intmathScore=resultSet.getInt("math");intenglishScore=resultSet.getInt("english");//输出CSV格式数据resp.getWriter().println(studentId+","+mathScore+","+englishScore+"<br>");}}//resultSet会在此处自动关闭}//7.connection和statement会在此处自动关闭1312.2.1SQL注入(清单12-2QueryServlet.java)3/3}catch(ClassNotFoundExceptione){resp.getWriter().println("错误:数据库驱动未找到。");System.err.println("数据库驱动未找到:"+e.getMessage());}catch(SQLExceptione){resp.getWriter().println("错误:数据库操作失败。");System.err.println("数据库操作异常:"+e.getMessage());}catch(Exceptione){resp.getWriter().println("错误:系统发生意外错误。");System.err.println("系统异常:"+e.getMessage());}}}1412.2.1SQL注入(代码示例)如果攻击者在用户名输入框中输入了'OR'1'='1,那么查询语句就会被篡改为:SELECT*FROMscoreWHEREstudent_id=""OR'1'='1'1512.2.1SQL注入如图12-1所示,由于'1'='1'总是为真,这个查询将会返回数据库中的所有用户,而不是只返回与输入的学号匹配的记录,显示结果如图12-2所示。http://localhost:8080/ch12/query_form.jsp图12-1SQL注入实例1612.2.1SQL注入http://localhost:8080/ch12/queryServlet图12-2SQL注入结果1712.2.1SQL注入防范SQL注入的措施◆使用预编译语句预编译语句(PreparedStatements)是防止SQL注入最有效的方法。例如,在MySQL数据库中,通过使用PreparedStatement语句,可以将SQL查询和参数分开,从而避免用户输入被解释为SQL代码的一部分。修改query_form.jsp页面为preparedquery_form.jsp,代码如清单12-3所示。1812.2.1SQL注入(清单12-3preparedquery_form.jsp)<%@pagecontentType="text/html;charset=UTF-8"language="java"%><html><head><title>防范SQL注入的措施</title></head><body><formmethod="POST"action="./preparedQueryServlet"><inputtype="text"name="student_id"value=""></input><inputtype="submit"value="查询"></input></form></body></html>1912.2.1SQL注入(清单12-4PreparedQueryServlet.java)1/3修改QueryServlet.java为PreparedQueryServlet.java,代码如清单12-4所示。packagecom.example.ch12;importjakarta.servlet.ServletException;importjakarta.servlet.annotation.WebServlet;importjakarta.servlet.http.HttpServlet;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.sql.*;//声明Servlet映射路径,处理/preparedQueryServlet的POST请求@WebServlet("/preparedQueryServlet")publicclassPreparedQueryServletextendsHttpServlet{@OverrideprotectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{try{//加载MySQLJDBC驱动Class.forName("com.mysql.cj.jdbc.Driver");//配置数据库连接参数Stringurl="jdbc:mysql://:3306/university";Stringusername="root";//数据库用户名Stringpassword="root";//数据库密码//建立数据库连接2012.2.1SQL注入(清单12-4PreparedQueryServlet.java)2/3Connectionconnection=DriverManager.getConnection(url,username,password);//获取请求参数Stringstudent_id=req.getParameter("student_id");//使用预处理语句防止SQL注入(安全实践)Stringquery="SELECT*FROMscoreWHEREstudent_id=?";PreparedStatementpstmt=connection.prepareStatement(query);pstmt.setString(1,student_id);//安全设置查询参数System.out.println("执行查询:"+query);//调试日志//执行查询ResultSetresultSet=pstmt.executeQuery();//设置响应内容类型resp.setContentType("text/plain;charset=UTF-8");//处理结果集while(resultSet.next()){//提取结果集字段(字段名与数据库列名对应)Stringid=resultSet.getString("student_id");//学生IDintmath=resultSet.getInt("math");//数学成绩intenglish=resultSet.getInt("english");//英语成绩//输出CSV格式结果resp.getWriter().println(id+","+math+","+english+"\n");}2112.2.1SQL注入(清单12-4PreparedQueryServlet.java)3/3//关闭数据库
pstmt.close();resultSet.close();connection.close();}catch(Exceptione){e.printStackTrace();}}}2212.2.1SQL注入上述代码运行结果如图12-3所示。http://localhost:8080/ch12/preparedquery_form.jsp图12-3PreparedStatement查询界面2312.2.1SQL注入如果提交的语句不合法没有显示结果,起到防止信息泄漏的效果,显示结果如图12-4所示。运行结果表明,通过使用PreparedStatement预处理语句和参数绑定的方式,将用户输入的学号作为参数安全地传递给SQL查询,有效防止了SQL注入攻击,同时保持了原有的学生成绩查询功能。图12-4SQL注入失败后的空结果页面2412.2.1SQL注入(1)◆使用存储过程存储过程是在数据库中预定义的一组SQL语句,它们可以通过调用数据库服务器上的函数执行。使用存储过程可以减少SQL注入的风险,因为它们通常不接受用户输入的SQL代码。◆输入验证和清理尽管预编译语句是防止SQL注入的首选方法,但验证和清理用户输入仍然是一个好习惯。可以使用正则表达式、白名单等方法验证输入数据是否符合预期格式。◆最小权限原则确保数据库用户只拥有执行其任务所需的最小权限。这可以防止攻击者即使成功注入了SQL代码,也无法执行危险的操作(如删除表或修改数据)。◆错误处理避免在应用程序中显示详细的数据库错误信息,因为这可能会泄露有关数据库结构和敏感信息。相反,应该记录这些信息并在内部进行调试。2512.2.1SQL注入(2)◆安全编码实践遵循安全编码实践,如避免在代码中硬编码敏感信息(如数据库凭据),尽量使用框架和库提供的安全功能等。通过实施上述措施,可以大大降低JavaWeb应用中SQL注入的风险。注意,安全是一个多层次的问题,需要综合考虑各种因素确保应用程序的健壮性和安全性。2612.2.2跨站脚本攻击跨站脚本攻击是一种常见的Web安全漏洞,攻击者通过在用户浏览器中执行恶意脚本,可以实现用户会话劫持、钓鱼攻击、恶意重定向等多种攻击方式。在JavaWeb应用中,XSS攻击通常分为反射型、存储型和DOM型三种。下面讲解在JavaWeb开发中,常见的XSS攻击实例。反射型XSS攻击实例反射型XSS攻击通常发生在用户输入的数据被立即反射回页面上,而没有进行适当的验证或过滤。例如,在一个JSP页面中,如果直接将用户通过URL参数传递的数据插入到HTML中,就可能导致反射型XSS攻击。◆实例代码清单12-5所示的代码是一个存在反射型XSS安全漏洞的JSP页面,直接将在URL参数中接收到的name值未经任何过滤就输出到HTML页面中,攻击者可以通过构造恶意name参数来注入并执行恶意脚本代码。2712.2.2跨站脚本攻击(清单12-5xss_reflected.jsp)<%@pagecontentType="text/html;charset=UTF-8"language="java"%><html><head><title>反射型XSS攻击</title></head><body><h1>欢迎,<%=request.getParameter("name")%>!</h1></body></html>2812.2.2跨站脚本攻击◆攻击方式攻击者可以通过构造特殊的URL触发XSS攻击,当用户访问这个URL时,浏览器会执行嵌入的JavaScript代码,弹出警告框,XSS攻击实例运行结果如图12-5所示。http://localhost:8080/ch12/xss_reflected.jsp?name=<script>alert('XSSAttack!')</script>图12-5XSS攻击实例运行结果2912.2.2跨站脚本攻击◆存储型XSS攻击实例存储型XSS攻击发生在用户输入的数据被存储在服务器上,并在后续请求中被读取并插入到HTML页面中。例如,在一个留言板应用中,如果攻击者在留言内容中插入了恶意脚本,那么所有查看该留言的用户都会执行这段脚本。◆实例代码清单12-6所示的代码是一个存在存储型XSS安全漏洞的JSP页面,直接从会话中获取留言内容并未经任何过滤就输出到HTML页面中,如果恶意脚本被存储到服务器会话中,所有访问该页面的用户都会受到攻击。3012.2.2跨站脚本攻击(清单12-6xss_store.jsp)<%@pagecontentType="text/html;charset=UTF-8"language="java"%><%Stringmessage=(String)request.getSession().getAttribute("message");%><html><head><title>存储型XSS攻击</title></head><body><h1>留言内容:</h1><p><%=message%></p></body></html>3112.2.2跨站脚本攻击(代码示例)◆攻击方式攻击者可以在留言内容中输入恶意脚本,例如:<script>alert('XSSAttack!')</script>然后,当其他用户查看该留言时,他们的浏览器将执行上述脚本。3212.2.2跨站脚本攻击◆DOM型XSS攻击实例
DOM型XSS攻击发生在客户端的JavaScript代码中,通过修改页面的DOM结构插入恶意脚本。这种攻击通常不直接涉及服务器端的代码,而是利用前端代码的漏洞。◆实例代码清单12-7所示的代码是一个存在DOM型XSS安全漏洞的JSP页面,通过JavaScript的innerHTML属性动态更新页面内容,同时还将URL参数直接输出到页面,攻击者可以通过构造恶意msg参数来注入并执行客户端脚本代码。3312.2.2跨站脚本攻击(清单12-7xss_attack.jsp)<%@pagecontentType="text/html;charset=UTF-8"language="java"%><html><head><title>DOM型XSS攻击</title><script>functionshowMessage(message){document.getElementById("message").innerHTML=message;}</script></head><body><h1>DOM型XSS攻击</h1><divid="message"></div><%=request.getParameter("msg")%></body></html>3412.2.2跨站脚本攻击◆攻击方式攻击者可以通过构造特殊的URL触发DOM型XSS攻击,例如:http://localhost:8080/ch12/xss_attack.jsp?msg=<imgsrc='x'onerror='alert()'/>当页面加载时,JavaScript代码会将恶意脚本插入到DOM中,并导致浏览器执行该脚本。另外,JSP表达式<%=request.getParameter("msg")%>直接输出未经过滤的参数到HTML中,也是导致漏洞产生的重要原因。◆防御措施为了防止XSS攻击,可以采取以下防御措施。3512.2.3跨站请求伪造跨站请求伪造(Cross-SiteRequestForgery,CSRF),也被称为One-clickAttack或者SessionRiding,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。下面是一个JavaWeb中CSRF攻击的实例。◆场景设定受害者Bob在银行有一笔存款,银行的网站地址为http://bank.example。黑客Mallory在该银行也有账户,并知道一个URL可以将钱进行转账操作,例如:http://bank.example/withdraw?account=bob&amount=1000000&for=bob2这个URL可以使Bob把1000000的存款转到bob2的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的session,并且该session的用户Bob已经成功登录。3612.2.3跨站请求伪造◆攻击过程假如用户Mallory自己直接发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory但是这个请求来自Mallory而非Bob,他不能通过安全认证,因此该请求不会起作用。Mallory决定使用CSRF的攻击方式。于是他制作了一个自己的网站,并在网站中植入如下链接:src="http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory"然后通过虚假广告等手段诱使Bob访问该链接,上述URL就会从Bob的浏览器发送至银行网站,而这个请求会携带Bob浏览器中的Cookie一起发送至银行服务器。如果Bob当时恰巧访问他的银行账号不久,他的浏览器与银行网站之间的session尚未过期,浏览器的Cookie之中含有Bob的认证信息,那么这个URL请求就会得到响应,银行账户内的余额将从Bob的账号转移到Mallory的账号,而Bob当时毫不知情。3712.2.3跨站请求伪造(1)◆攻击结果当Bob发现账户余额减少,即使到银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有发现任何被攻击的痕迹。◆防御措施为了防御CSRF攻击,可以采取以下措施:3812.2.3跨站请求伪造(2)◆验证HTTPReferer字段根据HTTP协议,在HTTP头中有一个称为Referer的字段,它记录了该HTTP请求的来源地址。银行网站可以对于每一个转账请求验证其Referer值,如果是以bank.example开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果Referer是其他网站的话,则有可能是黑客的CSRF攻击,拒绝该请求。但这种方法并非万无一失,因为Referer的值是由浏览器提供的,也存在攻击者伪造Referer字段的可能性。◆在请求地址中添加TOKEN并验证由于验证HTTPReferer字段的方法并不可靠,因此可以在HTTP请求中以参数的形式加入一个随机产生的TOKEN,并在服务器端建立一个拦截器验证这个TOKEN。如果请求中没有TOKEN或者TOKEN内容不正确,则认为可能是CSRF攻击而拒绝该请求。这种方法要比检查Referer更加安全。【注意】Referer的正确英语拼法是Referrer。由于早期HTTP规范的拼写错误,为了保持向后兼容就将错就错了。其他网络技术的规范企图修正此问题,使用了正确拼法,所以拼法不统一。3912.2.4URL操作攻击(1)在JavaWeb应用中,URL操作攻击通常指的是通过构造或篡改URL,从而利用应用的安全漏洞。下面是一个URL操作攻击的实例,它结合了参数篡改和潜在的安全漏洞。◆场景设定假设有一个JavaWeb应用,允许用户查看和编辑自己的个人信息。查看信息的URL结构如下所示:/user/profile?userId=123其中,userId参数用于指定要查看或编辑的用户ID。◆攻击过程◆参数篡改攻击者注意到URL中的userId参数,并尝试将其篡改为其他值,例如:/user/profile?userId=456如果应用没有实施适当的权限检查,攻击者能够通过漏洞查看或编辑其他用户的个人信息。4012.2.4URL操作攻击(2)◆利用安全漏洞假设应用存在安全漏洞,如未对用户输入进行验证或过滤,攻击者能够利用上述漏洞构造恶意URL。例如,攻击者可能尝试注入SQL代码或执行其他类型的注入攻击,以获取数据库中的敏感信息或执行其他恶意操作。◆绕过身份验证在某些情况下,攻击者可能尝试通过篡改URL绕过身份验证机制。例如,如果应用使用URL参数传递会话令牌或身份验证令牌,并且这些令牌没有受到适当的保护,在一定条件下,攻击者能够捕获这些令牌并使用它们访问受保护的资源。◆攻击结果•数据泄露:攻击者查看其他用户的个人信息,如姓名、电子邮件地址、电话号码等。•数据篡改:攻击者编辑其他用户的个人信息,导致数据不准确或损坏。•账户劫持:攻击者绕过身份验证机制,访问其他用户的账户并执行恶意操作。4112.2.4URL操作攻击(3)防御措施为了防御URL操作攻击,可以采取以下措施。◆输入验证和过滤:对URL参数和其他用户输入进行严格的验证和过滤,以防止注入攻击和其他类型的攻击。◆权限检查:确保只有具有适当权限的用户才能访问或修改特定资源。这可以通过实施基于角色的访问控制(RBAC)或其他权限管理策略实现。◆使用安全的会话管理:确保会话令牌和身份验证令牌受到适当的保护,例如使用HTTPS进行加密传输,并避免在URL中传递这些令牌。◆安全审计和监控:定期审计和监控应用的安全性,以检测潜在的安全漏洞和攻击行为。4212.2.5密码保护与验证在JavaWeb应用中,实现密码保护通常涉及对用户密码的加密存储和验证过程。下面实例演示JavaWeb密码保护,包括密码的加密存储和验证。◆添加依赖首先,需要引入一个加密库处理密码的哈希运算。对于用户的密码,需要加密后存储在数据库中,如果采用明文存储,一旦发生数据库泄漏事件,将会完全泄漏所有用户的密码,这将会引起非常大的负面影响。本书使用BCrypt算法处理密码,在pom.xml中添加依赖信息如清单12-8所示。4312.2.5密码保护与验证(清单12-8pom.xml)...<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-crypto</artifactId><version>6.2.0</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.3.0</version></dependency>...4412.2.5密码保护与验证◆用户注册和登录在用户注册时,BCrypt会自动生成一个随机盐值,将密码与盐值组合并哈希,然后将包含了盐值信息的哈希字符串存储在数据库中。在用户登录时,只需从数据库中检索出该哈希字符串,并调用encoder.matches(password,storedHash)方法。BCrypt会自动从哈希字符串中提取出当初使用的盐值,对用户输入的密码执行相同的哈希计算,并安全地比较两者是否一致。下面实例演示如何实现用户注册和登录功能。清单12-9所示的代码是使用SpringSecurityBCrypt密码编码器实现安全密码管理的服务类,通过哈希加密存储用户密码并在登录时进行安全验证,有效防止密码明文存储和彩虹表攻击,提供了完整的用户注册和登录认证功能。4512.2.5密码保护与验证(清单12-9PasswordService.java)1/2packagecom.example.ch12;//1.导入SpringSecurity的密码编码器接口和BCrypt实现importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importjava.util.HashMap;importjava.util.Map;/***密码服务类*/publicclassPasswordService{//2.创建一个BCrypt编码器实例(线程安全,可以作为静态常量)//它将用于哈希和验证密码privatestaticfinalPasswordEncoderencoder=newBCryptPasswordEncoder();//创建一个静态Map来模拟用户数据库,使注册和登录逻辑协同工作//在实际应用中,这里应该是一个DAO层,用于操作数据库。privatestaticfinalMap<String,String>userDatabase=newHashMap<>();/***注册用户(使用BCrypt哈希密码)*/publicStringregisterUser(Stringusername,Stringpassword){//3.使用BCrypt对原始密码进行哈希//BCrypt会自动生成一个随机盐值,并将盐值包含在最终的哈希字符串中4612.2.5密码保护与验证(清单12-9PasswordService.java)2/2StringhashedPassword=encoder.encode(password);System.out.println("hashedPassword:"+hashedPassword);//将用户名和哈希密码存入模拟数据库userDatabase.put(username,hashedPassword);returnhashedPassword;}/***用户登录验证(使用BCrypt验证)*/publicbooleanloginUser(Stringusername,Stringpassword){//1.从模拟数据库中根据username检索出用户存储的哈希密码StringstoredHash=userDatabase.get(username);//2.检查用户是否存在if(storedHash==null){System.out.println("Fail:"+username+"noexist.");returnfalse;}//3.使用BCrypt的matches方法验证//它会自动从storedHash中提取盐值,并对用户输入的password进行哈希,然后比较booleanisMatch=encoder.matches(password,storedHash);System.out.println(username+"Login:"+isMatch);returnisMatch;}}4712.2.5密码保护与验证【注意】上述代码是为了演示目的而简化的。在实际应用中,需要处理更多的细节,如异常处理、数据库连接管理、用户输入验证等。此外,为了增强安全性,可能还需要实施其他措施,如使用更强的哈希算法、限制密码尝试次数、实施双因素认证等。4812.2.5密码保护与验证◆编写Servlet测试编写两个Servlet分别测试注册和登录功能。◆注册测试Servlet类清单12-10所示的代码是用户注册Servlet,通过调用PasswordService的注册功能将用户名和密码进行BCrypt哈希加密存储,并在响应中返回原始密码和加密后的哈希值,用于演示安全的密码存储流程。4912.2.5密码保护与验证(清单12-10PasswordRegisterServlet.java)packagecom.example.ch12;importjakarta.servlet.ServletException;importjakarta.servlet.annotation.WebServlet;importjakarta.servlet.http.HttpServlet;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importjava.io.IOException;//定义Servlet访问路径为/testRegisterServlet@WebServlet("/testRegisterServlet")publicclassPasswordRegisterServletextendsHttpServlet{@Overrideprotectedvoidservice(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{PasswordServicetest=newPasswordService();//假设注册的用户名为fucz,密码为fucz123Stringusername="fucz";Stringpassword="fucz123";Stringpsw=test.registerUser(username,password);resp.getWriter().println("username:"+username+"\npassword:"+password+"\nhash-password:"+psw);}}5012.2.5密码保护与验证上述代码运行结果如图12-6所示。http://localhost:8080/ch12/testRegisterServlet图12-6用户注册密码加密的运行结果5112.2.5密码保护与验证◆登录测试Servlet类清单12-11所示的代码是用户登录验证Servlet,通过调用PasswordService的登录功能验证用户名和密码是否匹配,使用BCrypt算法安全比对哈希值,并在响应中返回登录验证结果。5212.2.5密码保护与验证(清单12-11PasswordLoginServlet.java)packagecom.example.ch12;importjakarta.servlet.ServletException;importjakarta.servlet.annotation.WebServlet;importjakarta.servlet.http.HttpServlet;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importjava.io.IOException;//定义Servlet访问路径为/testLoginServlet@WebServlet("/testLoginServlet")publicclassPasswordLoginServletextendsHttpServlet{@Overrideprotectedvoidservice(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{PasswordServicetest=newPasswordService();//假设用户输入用户和密码进行登录booleanislogin=test.loginUser("fucz","fucz123");resp.getWriter().println("islogin:"+islogin);}}5312.2.5密码保护与验证上述代码运行结果如图12-7所示。http://localhost:8080/ch12/testLoginServlet图12-7用户登录验证的运行结果5412.3验证码功能的应用在Web应用程序中实现验证码(CAPTCHA)是一种常见的安全措施,用于防止自动化脚本(如机器人)进行恶意操作,例如表单提交、暴力破解等。下面实例演示验证码实现步骤,使用了Servlet和JSP。如图12-8所示,在学者网注册页面中,实现了验证码功能,防止用户利用机器自动提交注册。图12-8验证码在注册页面中的应用实例5512.3.1生成验证码图片importjava.awt.*;importjava.awt.image.*;importjavax.imageio.*;◆引入必要的包【注意】javax.imageio未迁移,该包是JavaSE的一部分,未被纳入JakartaEE命名空间迁移范围,因此仍使用javax.imageio命名空间。5612.3.1生成验证码图片◆创建一个图像:如下代码创建了一个指定宽度和高度的RGB彩色图像缓冲区对象,使用TYPE_INT_RGB图像类型表示每个像素由红、绿、蓝三原色组成,为后续的图像处理和验证码生成提供了画布基础。BufferedImagebufferedImage=newBufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);5712.3.1生成验证码图片(代码示例)Graphics2Dg2d=bufferedImage.createGraphics();◆从图像中获取画布:如下代码从BufferedImage对象创建了一个Graphics2D图形上下文,提供了丰富的2D绘图功能,用于在图像缓冲区上进行文本绘制、图形渲染和验证码生成等操作。5812.3.1生成验证码图片(代码示例)g2d.setColor(Color.WHITE);g2d.fillRect(0,0,WIDTH,HEIGHT);◆将画布设置为白色,并按照尺寸设置画布大小:如下代码将图形上下文的绘制颜色设置为白色,并用白色填充整个图像区域,实现清空画布和设置白色背景的功能,为后续绘制验证码内容提供干净的绘制基础。5912.3.1生成验证码图片(代码示例)Randomrand=newRandom();StringBuildercaptchaText=newStringBuilder(CODE_COUNT);//产生验证码的字符集,为了避免混淆,去掉相似的字符,例如字母O与数字0等。char[]CHARS="ABCDEFGHJKMNPQRSTUVWXYZ23456789".toCharArray();for(inti=0;i<CODE_COUNT;i++){charrandomChar=CHARS[rand.nextInt(CHARS.length)];captchaText.append(randomChar);//绘制随机字符g2d.setFont(newFont("Arial",Font.BOLD,FONT_HEIGHT));g2d.setColor(newColor(rand.nextInt(256),rand.nextInt(256),rand.nextInt(256)));g2d.drawString(String.valueOf(randomChar),X_OFFSET+i*26,Y_OFFSET+FONT_HEIGHT-5);}◆生成指定长度的随机字符串,并在画布上绘制对应字符:如下代码生成指定长度的验证码字符串,从排除易混淆字符的字符集中随机选择字符,并使用随机颜色和粗体字体将每个字符绘制到图像上,形成彩色验证码图像。6012.3.1生成验证码图片(代码示例)//添加噪点for(inti=0;i<200;i++){intx=rand.nextInt(WIDTH);inty=rand.nextInt(HEIGHT);intrgb=rand.nextInt(0xffffff);bufferedImage.setRGB(x,y,rgb);}◆在图像前景上绘制若干燥点:如下代码在验证码图像上添加200个随机位置的彩色噪点,通过在每个随机坐标位置设置随机RGB颜色值来干扰机器识别,增强验证码的安全性和抗破解能力。6112.3.1生成验证码图片(代码示例)request.getSession().setAttribute("captcha",captchaText.toString());◆在session中保存验证码文本:如下代码将生成的验证码文本字符串存储到当前用户的会话中,作为后续验证用户输入的比对依据,确保验证码与特定会话关联且具有时效性。6212.3.1生成验证码图片(代码示例)◆将生成的图像写到输出流中:如下代码获取HTTP响应的输出流,并将生成的验证码图像以JPEG格式写入输出流,最终将图像发送到客户端浏览器显示给用户。ServletOutputStreamout=response.getOutputStream();ImageIO.write(bufferedImage,"jpg",out);6312.3.1生成验证码图片(代码示例)out.flush();out.close();//释放图形对象g2d.dispose();◆进行后续清理工作:如下代码执行资源清理操作:强制刷新输出流确保所有图像数据发送到客户端,关闭输出流释放网络资源,并销毁图形上下文对象回收系统内存,完成验证码生成的完整生命周期管理。6412.3.1生成验证码图片(清单12-12CaptchaServlet.java)1/4◆完整代码实现首先,创建一个生成验证码图片的类,代码如清单12-12所示。packagecom.example.ch12;importjava.awt.*;importjava.awt.image.*;importjavax.imageio.*;importjava.io.IOException;importjava.util.Random;importjakarta.servlet.ServletException;importjakarta.servlet.ServletOutputStream;importjakarta.servlet.annotation.WebServlet;importjakarta.servlet.http.HttpServlet;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;@WebServlet("/captcha")publicclassCaptchaServletextendsHttpServlet{privatestaticfinallongserialVersionUID=1L;privatestaticfinalintWIDTH=160;privatestaticfinalintHEIGHT=40;privatestaticfinalintCODE_COUNT=6;privatestaticfinalintFONT_HEIGHT=30;privatestaticfinalintX_OFFSET=10;privatestaticfinalintY_OFFSET=10;privatestaticfinalchar[]CHARS="ABCDEFGHJKMNPQRSTUVWXYZ23456789".toCharArray();6512.3.1生成验证码图片(清单12-12CaptchaServlet.java)2/4privatestaticfinalRandomrand=newRandom();@OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{//设置响应头部response.setContentType("image/jpeg");response.setHeader("Cache-Control","no-store,no-cache,must-revalidate");//HTTP1.1response.setHeader("Pragma","no-cache");//HTTP1.0response.setDateHeader("Expires",0);//Proxies//创建图像BufferedImagebufferedImage=newBufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);Graphics2Dg2d=bufferedImage.createGraphics();//填充背景g2d.setColor(Color.WHITE);g2d.fillRect(0,0,WIDTH,HEIGHT);//绘制边框g2d.setColor(Color.BLACK);g2d.drawRect(0,0,WIDTH-1,HEIGHT-1);//生成随机字符StringBuildercaptchaText=newStringBuilder(CODE_COUNT);for(inti=0;i<CODE_COUNT;i++){charrandomChar=CHARS[rand.nextInt(CHARS.length)];6612.3.1生成验证码图片(清单12-12CaptchaServlet.java)3/4captchaText.append(randomChar);//绘制随机字符g2d.setFont(newFont("Arial",Font.BOLD,FONT_HEIGHT));g2d.setColor(newColor(rand.nextInt(256),rand.nextInt(256),rand.nextInt(256)));g2d.drawString(String.valueOf(randomChar),X_OFFSET+i*26,Y_OFFSET+FONT_HEIGHT-5);}//添加噪点for(inti=0;i<200;i++){intx=rand.nextInt(WIDTH);inty=rand.nextInt(HEIGHT);intrgb=rand.nextInt(0xffffff);bufferedImage.setRGB(x,y,rgb);}//在session中保存验证码文本request.getSession().setAttribute("captcha",captchaText.toString());//返回图片6712.3.1生成验证码图片(清单12-12CaptchaServlet.java)4/4ServletOutputStreamout=response.getOutputStream();ImageIO.write(bufferedImage,"jpg",out);out.flush();out.close();//释放图形对象g2d.dispose();}}6812.3.1生成验证码图片上述Servlet的核心任务是生成一张包含随机文本的图片。它使用java.awt.Graphics2D在BufferedImage画布上绘制矩形、噪点和随机字符。最关键的步骤有两个:1)存储答案:request.getSession().setAttribute("captcha",...)。它将生成的随机字符串(如"KX2V6B")存储在服务器的Session中,以便后续CaptchaVerifyServlet进行校验。2)发送图片:ImageIO.write(bufferedImage,"jpg",out)。它将内存中的图像数据作为JPEG格式,通过response的输出流直接发送给浏览器。浏览器端的<img>标签正是通过src="captcha"访问了这个Servlet。6912.3.2验
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年江苏省扬中市《行测》考试考前冲刺密卷含答案详解(精练)
- (2026版)机关效能自查报告
- 2025年湖北省松滋市《行测》考试考前冲刺试卷【全优】附答案详解
- 燃料值班员岗中行业知识考核试卷含答案
- 密闭鼓风炉备料工岗位合规考核试卷含答案
- 数控水射流切割机操作工诚信品质能力考核试卷含答案
- 2026年全南县事业单位人员招聘笔试备考试题及答案解析
- 2026年东乡族自治县公务员招聘考试备考试题及答案解析
- 2026年费县公务员招聘笔试备考题库及答案解析
- 2026年疏勒县公务员招聘笔试备考题库及答案解析
- NCIC临床实践指南:免疫检查点抑制剂毒性管理指南(2026版)课件
- 旋挖钻孔灌注桩施工方案模板
- 安徽省合肥市普通高中六校联盟2025-2026学年高二上学期11月期中考试英语试卷(含答案)
- 2025海南国资运营旗下国改基金公司招聘4人笔试历年典型考点题库附带答案详解
- Meckel憩室课件教学课件
- 水利水电工程土建施工安全技术规程
- 《江苏省车险、非车财险行业自律公约(2025年补充一)》
- 2025年山西英语导游证考试题目及答案
- 普通车床设备说明书及操作要点
- 2025陕西西安航天基地公办学校合同制人员招聘111人考试参考题库及答案解析
- 2025至2030年中国海南省医院行业市场深度评估及投资战略规划报告
评论
0/150
提交评论