目录
一、web四大域。
(1)基本介绍。
- 在JavaWeb开发中,"四大域"是指四种不同的作用域(scope),它们用于存储和管理用户会话中的数据。
(2)RequestScope。(请求域)
- 作用域限定在"一次请求"中。即一个Servlet处理一个客户端请求的过程中。
其数据存储在HttpServl
etRequest对象中。
常用于存储一次请求中需要的数据,比如表单提交的数据。
使用次数:较多。
(3)SessionScope。(会话域)
- 作用域限定在"一个用户会话"中。即从用户打开浏览器到关闭浏览器的整个过程中。
- 数据存储在HttpSession对象中。
- 常用于存储用户登录状态、用户偏好设置等需要跨多个请求保持的数据。
- 使用次数:多。
(4)ApplicationScope。(应用域)
- 作用域限定在"整个Web应用程序"中。即所有用户共享。
- 数据存储在ServletContext对象ServletContext中。
- 常用于存储全局信息。(比如配置信息、应用级别的计数器等)
- 使用次数:较少。
(5)PageScope。(页面域)
- 作用域限定在"JSP页面的生命周期"内。即从JSP页面被请求到页面渲染完成的过程中。
- 数据存储在JSP页面的隐式对象pageContext中。
- 常用于存储JSP页面内的数据,比如JSP标签和脚本片段之间的数据共享。
- 使用次数:较少。
二、HttpSession原理。
(1)Session的基本介绍。
- 在JavaWeb开发中,Session通常指的是HttpSession接口。
- HttpSession是Java Servlet API提供的一个接口。
- HttpSession的对象代表了客户端与服务器端之间的一个会话。用于在客户端和服务器端之间保持状态,即跟踪用户的会话。
(2)会话是什么?
1、会话的基本介绍。
- 在Web开发中,会话(Session)是指服务器与客户端之间的一次连续的交互过程,它允许服务器跟踪和存储用户的状态信息。
- 客户端打开浏览器,访问服务器,表示会话开始。只要浏览器不关闭,本次会话一直存在。而且这次的会话的所有请求,全部共享一个Session域。(request域对象,只在用一次请求有效,作用域小)
- 当客户端关闭浏览器,表示会话的结束。
- 每一个客户端都有自己的会话。(Session)
2、会话的生命周期。
- 会话有开始和结束。
- 通常:从用户第一次与服务器交互时创建,直到用户关闭浏览器。
- 其它情况:会话超时或服务器配置的最大会话时间限制。
(3)HttpSession接口API。
- 查阅javaEE接口文档。
1、HttpSession域对象功能。
- 如下都是Session的重要方法。
- 其中有几个四大域对象都有的方法。其它的是Session域对象特有的特性与方法。
2、HttpSession特有方法与属性。
- SessionID:Session的唯一标识。
- setMaxInactiveInterval(int):最大间隔时间意思就是该Session从上一次使用到下一次使用的间隔。
- invalidate():让当前Session失效!用户的退出登录(注销)功能实现就是让当前Session域失效!
- 注销类"LogOutServlet"代码如下。
javapackage com.fs.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; /** * @Title: LoginService * @Author HeYouLong * @Package com.fs.web * @Date 2024/11/25 下午9:16 * @description: 退出登录处理 */ @WebServlet("/logout") public class LogOutServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //让session失效 req.getSession().invalidate(); //重定向到登录界面 resp.sendRedirect("/login.jsp"); } }
(4)HttpSession底层原理。
1、Session的最基本特征。
- 创建于服务器,保存于服务器。
- 每一个用户都有自己的Session对象。(Session不是共享的)
- 底层依赖于------Cookie实现。
2、Session底层图解。
- 服务器为每一个客户端创建Session对象时,都会生成唯一的SessionID。
- 响应给客户端时,将SessionID存储在Cookie中。注意SessionID是加密的。Cookie之后的每一次请求会自动的将SessionID发送给服务器。
- 服务器拿到SessionID进行解析并寻找对应的Session对象。
3、Session底层解释。
- Session底层是依赖Cookie的!
- 注意Session无法跨浏览器的。
- 当我首次去银行时,因为还没有账号,所以需要开一个账号,我获得的是银行卡,而银行这边的数据库中留下了我的账号,我的钱是保存在银行的账号中,而我带走的是我的卡号。
- 当我再次去银行时,只需要带上我的卡,而无需再次开一个账号了。只要带上我的卡,那么我在银行操作的一定是我的账号!
- 当首次使用Session时,服务器端要创建Session对象。Session是保存在服务器端,而给客户端的是SessionID(一个cookie中保存SessionID)。客户端带走的是SessionID,而数据是保存在Session对象中。
- 当客户端再次访问服务器时,在请求中会带上SessionID。而服务器会通过SessionID找到对应的Session对象,而无需再创建新的Session对象。
4、Session自动失效与手动失效。
- 开发人员可以通过调用Session对象的的invalidate()方法来手动使Session失效。这通常在用户注销或退出登录时使用。
- Session对象会有一个默认的超时时间,如果用户在这段时间内没有任何操作,Session将会自动失效。
- 当服务器重启或重启应用时,所有的Session对象都会被销毁,导致所有用户的Session失效。或者关闭浏览器通常也会导致Session失效。
三、使用Session实现验证码与注销功能。
(1)前端jsp:表单项-验证码的代码。
- 注意生成验证码图片的功能是通过代码完成。(访问指定的"/XXXServlet")
- 注意:css样式使用的是bootstrap提供的样式与组件。通过<link>标签在线引入。
html<div class="form-inline"> <label for="vcode">验证码:</label> <input type="text" name="verifycode" class="form-control" id="verifycode" placeholder="请输入验证码" style="width: 120px;"/> <a href="javascript:refreshCode()"><img src="/checkCode" title="看不清点击刷新" id="vcode"/></a> </div>
- 刷新验证码时,触发点击事件,调用方法refreshCode()。代码如下。
javascript<script type="text/javascript"> //js定义函数 function refreshCode(){ //不做下面处理,就会走浏览器缓存, url没有变化, 导致使用上一次缓存内容,不会刷新验证码 //选择器 #id名------>获取到img标签 //使用attr()设置属性"src"值。 //加一个时间戳, 每次都不一样, 浏览器发现参数不一样, 误以为是新请求,不走缓存,就会刷新 $("#vcode").attr("src","/checkCode?"+new Date().getTime()); } </script>
(2)生成验证码的Servlet类。
javapackage com.fs.web; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 验证码 */ @WebServlet("/checkCode") public class CheckCodeServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { //服务器通知浏览器不要缓存 response.setHeader("pragma","no-cache"); response.setHeader("cache-control","no-cache"); response.setHeader("expires","0"); //在内存中创建一个长80,宽30的图片,默认黑色背景 //参数一:长 //参数二:宽 //参数三:颜色 int width = 80; int height = 30; BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); //获取画笔 Graphics g = image.getGraphics(); //设置画笔颜色为灰色 g.setColor(Color.GRAY); //填充图片 g.fillRect(0,0, width,height); //产生4个随机验证码,12Ey String checkCode = getCheckCode(); //将验证码放入HttpSession中。 //这里很重要,因为登录请求时,需要拿取服务器生成的验证码与用户输入的验证码进行校验! request.getSession().setAttribute("CHECKCODE_SERVER",checkCode); //设置画笔颜色为黄色 g.setColor(Color.YELLOW); //设置字体的小大 g.setFont(new Font("黑体",Font.BOLD,24)); //向图片上写入验证码 g.drawString(checkCode,15,25); //将内存中的图片输出到浏览器 //参数一:图片对象 //参数二:图片的格式,如PNG,JPG,GIF //参数三:图片输出到哪里去 ImageIO.write(image,"PNG",response.getOutputStream()); } /** * 产生4位随机字符串 */ private String getCheckCode() { String base = "0123456789ABCDEFGabcdefg"; int size = base.length(); Random r = new Random(); StringBuffer sb = new StringBuffer(); for(int i=1;i<=4;i++){ //产生0到size-1的随机值 int index = r.nextInt(size); //在base字符串中获取下标为index的字符 char c = base.charAt(index); //将c放入到StringBuffer中去 sb.append(c); } return sb.toString(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } }
(3)运行效果图。
(4)最终验证码的实现步骤。
1、第一次请求与响应。
- 第一次请求:客户端请求login.jsp(登录页面)。
- 服务器第一次响应。在客户端显示login.jsp(登录页面)。
2、第二次请求与响应。
- 第二次请求"XXXServlet":请求客户端登录界面中的随机验证码图片。
- 服务器第二次响应。并将生成的随机验证码存储在Session域中。
- 为什么验证码不存储在Cookie中?不安全、无意义。因为Cookie暴露在客户端,用户可以查看和修改!
- 随后在登录界面的验证码位置显示生成的验证码图片。
3、第三次请求与响应。
- 第三次请求:用户在客户端填写(用户名、密码、验证码等)后,点击提交"登录"按钮。也就是登录请求。("/LoginServlet")
- 服务器处理请求。首先判断验证码是否正确!!
- 如何校验用户输入的验证码与服务器响应的验证码一致?
- 在第二次请求与响应中,服务器将生成的随机验证码存储在Session中。因为验证码每生成一次就不能让用户去更改,所以存储在服务器(域对象),而不是存储在客户端
- 使用Session技术!而不使用Request域。是因为不是同一次请求,拿不到数据。
- 通过会话(Session)技术可以实现该功能。
- 第三次请求服务器拿到用户提交的验证码与Session域获取到的验证码进行比对即可。
- 通过HttpServletRequest对象的getSession()方法获取到HttpSession对象。
- 再通过HttpSession对象的getAttribute("键名")得到对应的键值(验证码)即可。
4、LoginServlet类代码。
- 其中涉及到"记住我"功能的实现。(使用Cookie技术)
javapackage com.fs.web; import com.fs.entity.LoginInfo; import com.fs.service.LoginService; import com.fs.service.impl.LoginServiceImpl; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; import java.util.Objects; /** * @Title: LoginService * @Author HeYouLong * @Package com.fs.web * @Date 2024/11/25 下午9:16 * @description: 登录处理 */ @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.设置post请求编码, 响应编码 req.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=UTF-8"); //进行验证码验证 //获取用户输入的验证码 String verifycode = req.getParameter("verifycode"); //获取服务器发来的验证码。与session存的验证码比对 HttpSession session = req.getSession(); String codeServer = (String) session.getAttribute("CHECKCODE_SERVER"); if (codeServer.equals(verifycode)) { //2.获取请求参数 String username = req.getParameter("user"); String password = req.getParameter("password"); //3.调用业务层LoginService的登录方法 LoginService loginService = new LoginServiceImpl(); LoginInfo loginInfo = loginService.login(username, password); //4.根据返回结果跳转到不同的资源 if (loginInfo != null) { //登录成功 //判断是否勾选记住我 String rem = req.getParameter("rem"); if (Objects.nonNull(rem)) { //设置Cookie Cookie cookie1 = new Cookie("user", username); Cookie cookie2 = new Cookie("password", password); //设置存活时间(单位:秒) //7天 cookie1.setMaxAge(60 * 60 * 24 * 7); cookie2.setMaxAge(60 * 60 * 24 * 7); //设置path为/ 项目下任何资源可以访问 cookie1.setPath("/"); cookie2.setPath("/"); //发送Cookie resp.addCookie(cookie1); resp.addCookie(cookie2); } //跳转之前把LoginInfo对象保存到session域中 session.setAttribute("loginUser",loginInfo); //重定向到index.html页面 resp.sendRedirect("/index.jsp"); } else { //登录失败 //转发到登录页面 req.setAttribute("error", "账号或密码错误!"); req.getRequestDispatcher("/login.jsp").forward(req, resp); } } else { //验证码错误 req.setAttribute("error", "验证码输入错误!"); req.getRequestDispatcher("/login.jsp").forward(req, resp); } } }