《Java Web程序设计任务教程》简要复盘:第五章 会话及其会话技术

it2023-06-04  81

《Java Web程序设计任务教程》简要复盘:第五章 会话及其会话技术

第五章:会话及其会话技术会话概述Cookie对象什么是CookieCookie 属性Cookie 规范Cookie API1.构造方法2.Cookie类的常用方法 Session对象什么是Session?HttpSession API超时管理 实例1.显示用户上次访问时间2.实现用户登录3.实现一次性验证码 作者:氯磷Rolin

目录:

第一章:网页开发基础(无)第二章:Java Web概述第三章:Servlet基础第四章:请求与响应第五章:会话及其会话技术第六章:JSP技术第七章:EL表达式和JSTL第八章:Servlet的高级特性第九章:JDBC第十章:数据库连接池与DBUtils工具第十一章:JSP开发模型第十二章:文件的上传和下载

第五章:会话及其会话技术

当用户通过浏览器访问Web应用时,通常情况下,服务器需要对用户的状态进行跟踪 会话跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

会话概述

Web应用中的会话过程类似于生活中打电话的过程,它指的是一个客户端与Web服务器之间连续发生的一系列请求和响应过程 例如:一个用户在某网站上整个的购物过程就是一个会话 虽然HttpServletRequest对象和ServletContext对象都可以对数据进行保存,但是两个对象都不适用于会话跟踪,原因有二:

客户端请求Web服务器时,针对每次HTTP请求,Web服务器都会创建一个HttpServletRequest对象,该对象只能保存本次传递的数据,比如购买和结账是两个操作,对应两个请求,因此在发送结账请求时,之前购买请求中的数据就会消失使用ServletContext对象保存数据时,由于同一个Web应用共享的是同一个ServletContext对象,因此当用户发送结算请求时,服务器无法分辨哪些商品是哪些用户购买的,而会对所有商品进行结算,这显然是不可行的

在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。

为了保存会话中产生的数据,在Servlet技术中,提供了两个用于保存会话数据的对象分别是Cookie和Session

Cookie对象

Cookie是一种会话技术,它用于将会话过程中的数据保存到用户的浏览器中,从而使到服务器可以更好的进行数据的交互

什么是Cookie

Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。

由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

服务器向客户端发送Cookie时,会在HTTP响应头字段中增加Set-Cookie响应头字段。 Set-Cookie头字段中设置的Cookie遵循着一定的语法格式: Set-Cookie: user=Rolin; Path=/; user表示Cookie的名称,Rolin表示Cookie的值,Path表示Cookie的属性 需要注意的是:Cookie必须以键值对的形式存在,其属性可以有多个,但这些属性之间必须用分号和空格间隔工作原理: 当用户第一次访问服务器时,服务器会在响应消息中增加Set-Cookie头字段,将用户信息以Cookie的形式发送给浏览器。一旦用户浏览器接受了服务器发送的Cookie信息,就会将其保存到缓冲区中。这样,当浏览器后续访问该服务器时,都会在请求消息中将用户信息以Cookie的形式发送给服务器,从而使服务器端分辨出当前请求是由哪个用户发出的。

Cookie 属性

Cookie的name属性一旦确定,即不可更改

Cookie常用属性 属性名描述String nameCookie的名称Object valueCookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码int maxAge该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1boolean secure该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为falseString path该Cookie的使用路径。如果设置为“/sessionWeb/”,则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/”String domain可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”String comment该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明int version该Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范

Cookie 规范

Http协议提供了有关Cookie的规范,如今市场上出现了大量的浏览器,一些浏览器对该Cookie规范进行了一些扩展,例如每个Cookie的大小为8KB,最多可保存500个Cookie等,但Cookie缓存文件不会占满硬盘空间。 HTTP协议的Cookie规范如下:

Cookie通过请求头和响应头在服务器与客户端之间传输。Cookie大小上限为4KB。一个服务器最多在客户端浏览器上保存20个Cookie。一个浏览器最多保存300个Cookie。

Cookie API

为了封装Cookie信息,在Servlet API中提供一个javax.servlet.http.Cookie类,包含了生成Cookie信息和提取Cookie信息各个属性的方法

1.构造方法

Cookie类有且仅有一个构造方法,具体语法: public Cookie (java.lang.String name , java.lang.String value);

其中参数name是Cookie的名称,value用于指定Cookie的值

2.Cookie类的常用方法

方法声明功能描述String getName()用于返回Cookie的名称void setValue(String newValue)用于为Cookie设置一个新的值String getValue()用于返回Cookie的值void setMaxAge(int expiry)用于设置Cookie在浏览器上保持有效的秒数int getMaxAge()用于返回Cookie在浏览器上保持有效的秒数void setPath(String url)用于设置Cookie项的有效目录路径String getPath()用于返回Cookie项的有效目录void setDomain(String pattern)用于设置该Cookie的有效域String getDomain()用于返回该Cookie的有效域void setVersion(int v)用于设置该Cookie采用的协议版本int getVersion()用于返回该Cookie采用的协议版本void setComment(String purpose)用于设置该Cookie项的注解部分String getComment()用于返回该Cookie的注解部分void setSecure(boolean flag)用于设置该Cookie项是否只能使用安全的协议传送boolean getSecure()用于返回该Cookie项是否只能使用安全的协议传送

Session对象

除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。 Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。

Session是一种将会话数据保存到服务器端的技术

什么是Session?

Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。

如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。

Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

Session保存用户信息的过程

HttpSession API

方法重载 Session是与每个请求消息紧密相关的,为此HttoServletRequest定义了用于获取Session对象的getSession()方法,该方法有两种重载形式: public HttpSession getSession(boolean create); public HttpSession getSession();

getSession(boolean create) 根据传递的参数来判断是否创建新的HttpSession对象 如果参数为true,则在相关的HttpSession对象不存在时创建并返回新的HttpSession对象 如果参数为false,则在相关HttpSession对象不存在时返回null

HttpSession接口中的常用方法

要想使用HttpSession对象管理会话数据,不仅需要获取到HttpSession对象,还需要了解其相关方法

方法声明功能描述String getId()返回当前Session的标识号long getCreationTime()返回创建Session的时间,该值是一个长整数long getLastAccessedTime()返回Session与客户端最后一次请求的时间void setMaxInactiveInterval(int interval)修改当前会话的默认超时间隔boolean isNew()判断当前Session是否新创建的void invalidate()强制使Session对象无效ServletContext getServletContext()返回一个代表当前Web程序的的ServletContext对象void setAttribite(String name,Object value)用于将一个对象与一个名称关联后存放到Session对象String getAttribute()用于从当前Seesion对象中返回指定名称的属性对象void removeAttribute(String name)用于从当前Session对象中删除指定名称的属性

超时管理

当客户端第一次访问某个能开启会话的资源时,Web服务器就会创建一个与该客户端对应的Session对象,即使客户端已经离开或者关闭了浏览器,Web服务器还要保留与之对应的HttpSession对象。随之时间的推移,这些不再使用的HttpSession对象会在Web服务器中累积的越来越多,从而使Web服务器的内存耗尽

为了解决上面的问题,Web服务器采用了超时限制的方法来判断客户端是否还在继续访问,如果超过一定时间客户端也没有来访问Web服务器,则将与该客户端会话所对应的Session对象变为垃圾对象,等待垃圾收集器彻底清除它。

有三种方案来管理超时的Session

配置tomcat全局超时管理 在tomcat服务器的 conf/web.xml 文件配置session的超时 时间是分钟为单位,此时配置session的超时管理适用于所有的web应用

<session-config> <session-timeout>20</session-timeout> </session-config>

配置当前应用程序的超时管理 在当前的web应用的web.xml文件中配置session的超时管理,会覆盖tomcat服务器的web.xml文件中的配置

<session-config> <!-- 配置session的超时管理,以分钟为单位 --> <session-timeout>30</session-timeout> </session-config>

通过session对象的方法实现超时管理 设置一个秒数,这个秒数表示客户端在不发出请求时,session被Servlet引擎维持的最长时间。

session.setMaxInactiveInterval(5);

三个方案的优先级:方案3>>方案2>>方案1 要想使Session失效,除了可以等待会话时间超时外,还可以通过invalidate()方法强制使会话无效

实例

1.显示用户上次访问时间

目标 当用户访问某些Web应用时,经常会显示出该用户上一次的访问时间。例如,QQ登录成功会后显示用户上次登录时间。具体实现 public class LastAccessServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 指定服务器输出内容的编码方式UTF-8,防止发生乱码 response.setContentType("text/html;charset=utf-8"); String lastAccessTime = null; // 获取所有的cookie,并将这些cookie存放在数组中 Cookie[] cookies = request.getCookies(); // 遍历cookies数组 for (int i = 0; cookies != null && i < cookies.length; i++) { if ("lastAccess".equals(cookies[i].getName())) { // 如果cookie的名称为lastAccess,则获取该cookie的值 lastAccessTime = cookies[i].getValue(); break; } } // 判断是否存在名称为lastAccess的cookie if (lastAccessTime == null) { response.getWriter().print("您是首次访问本站!!!"); } else { response.getWriter().print("您上次的访问时间是: " + lastAccessTime); } // 创建cookie,将当前时间作为cookie的值发送给客户端 String currentTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss") .format(new Date()); Cookie cookie = new Cookie("lastAccess",currentTime); //cookie.setMaxAge(60*60); //设置cookie最大存在时间 // 发送 cookie response.addCookie(cookie); } public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } }

2.实现用户登录

任务目标 如何使用Session技术实现用户登录的功能。 当用户访问某个网站的首页面时,首先会判断用户是否登录,如果已经登录则在首界面中显示用户登录信息,否则进入登陆页面,完成用户登录功能,然后显示用户登录信息。在用户登录的情况下,如果单击用户登录界面中的”退出“时,就会注销当前用户,返回主界面。

流程图

具体实现:

创建封装用户信息类:

package com.Rolin.session.userLogin; public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }

创建servlet: (1)该Servlet用于显示网站的首界面

package com.Rolin.session.userLogin; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; public class IndexServlet extends HttpServlet { public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { // 解决乱码问题 response.setContentType("text/html;charset=utf-8"); // 创建或者获取保存用户信息的Session对象 HttpSession session = request.getSession(); User user = (User) session.getAttribute("user"); if (user == null) { response.getWriter().print( "您还没有登录,请<a href='/chapter05/login.html'>登录</a>"); } else { response.getWriter().print("您已登录,欢迎你," + user.getUsername() + "!"); response.getWriter().print( "<a href='/chapter05/LogoutServlet'>退出</a>"); // 创建Cookie存放Session的标识号 Cookie cookie = new Cookie("JSESSIONID", session.getId()); cookie.setMaxAge(60 * 30); cookie.setPath("/chapter05"); response.addCookie(cookie); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

(2)该Servlet用于显示用户登录成功后的界面

package cn.itcast.chapter05.session.example02; import java.io.*; import javax.servlet.ServletException; import javax.servlet.http.*; public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); PrintWriter pw = response.getWriter(); //假设正确的用户名 是itcast 密码是123 if (("itcast").equals(username) && ("123").equals(password)) { User user = new User(); user.setUsername(username); user.setPassword(password); request.getSession().setAttribute("user", user); response.sendRedirect("/chapter05/IndexServlet"); } else { pw.write("用户名或密码错误,登录失败"); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } (3)该Servlet用于完成用户注销功能 ```java package com.Rolin.session.userLogin; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; public class LogoutServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 将Session对象中的User对象移除 request.getSession().removeAttribute("user"); response.sendRedirect("/chapter05/IndexServlet"); } public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { doGet(request, response); } } ``` 3.创建登录页面 ```html <!DOCTYPE> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <form name="reg" action="/Rolin/LoginServlet" method="post"> 用户名: <input name="username" type="text" /><br/> 密&nbsp;&nbsp;码:<input name="password" type="password" /><br/> </form> </body> </html> ```

3.实现一次性验证码

目标 为了保护用户信息的安全,都会在网页登录的界面中添加一次性验证码,从而限制人们使用软件来暴力猜测密码。具体实现

本实例将基于第二个实例增加功能而来

修改表单页面,增加验证码输入框和验证码图片框 <form name="reg" action="/chapter05/LoginServlet" method="post"> 用户名: <input name="username" type="text" /><br/>&nbsp;&nbsp;码:<input name="password" type="password" /><br/> 验证码:<input type="text" name="check_code"> <img src="/chapter05/CheckServlet"><br> <input type="submit" value="提交" id="bt" /> </form> 编写CheckServlet,用于绘制验证码图片,其具体实现方式如下: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.awt.*; import java.awt.image.*; import javax.imageio.ImageIO; public class CheckServlet extends HttpServlet { private static int WIDTH = 60; //验证码图片宽度 private static int HEIGHT = 20; //验证码图片高度 public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{ HttpSession session = request.getSession(); response.setContentType("image/jpeg"); ServletOutputStream sos = response.getOutputStream(); //设置浏览器不要缓存此图片 response.setHeader("Pragma","No-cache"); response.setHeader("Cache-Control","no-cache"); response.setDateHeader("Expires", 0); //创建内存图象并获得其图形上下文 BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); //产生随机的认证码 char [] rands = generateCheckCode(); //产生图像 drawBackground(g); drawRands(g,rands); //结束图像的绘制过程,完成图像 g.dispose(); //将图像输出到客户端 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ImageIO.write(image, "JPEG", bos); byte [] buf = bos.toByteArray(); response.setContentLength(buf.length); //下面的语句也可写成:bos.writeTo(sos); sos.write(buf); bos.close(); sos.close(); //将当前验证码存入到Session中 session.setAttribute("check_code",new String(rands)); //直接使用下面的代码将有问题,Session对象必须在提交响应前获得 //request.getSession().setAttribute("check_code",new String(rands)); } //生成一个4字符的验证码 private char [] generateCheckCode() { //定义验证码的字符表 String chars = "0123456789abcdefghijklmnopqrstuvwxyz"; char [] rands = new char[4]; for(int i=0; i<4; i++) { int rand = (int)(Math.random() * 36); rands[i] = chars.charAt(rand); } return rands; } private void drawRands(Graphics g , char [] rands) { g.setColor(Color.BLACK); g.setFont(new Font(null,Font.ITALIC|Font.BOLD,18)); //在不同的高度上输出验证码的每个字符 g.drawString("" + rands[0],1,17); g.drawString("" + rands[1],16,15); g.drawString("" + rands[2],31,18); g.drawString("" + rands[3],46,16); System.out.println(rands); } private void drawBackground(Graphics g) { //画背景 g.setColor(new Color(0xDCDCDC)); g.fillRect(0, 0, WIDTH, HEIGHT); //随机产生120个干扰点 for(int i=0; i<120; i++) { int x = (int)(Math.random() * WIDTH); int y = (int)(Math.random() * HEIGHT); int red = (int)(Math.random() * 255); int green = (int)(Math.random() * 255); int blue = (int)(Math.random() * 255); g.setColor(new Color(red,green,blue)); g.drawOval(x,y,1,0); } } } 对LoginServlet进行修改,增加对于验证码的判断,修改完后的文件如下: package cn.itcast.chapter05.session.example02; import java.io.*; import javax.servlet.ServletException; import javax.servlet.http.*; public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); String checkCode = request.getParameter("check_code"); String savedCode = (String) request.getSession().getAttribute( "check_code"); PrintWriter pw = response.getWriter(); if (("itcast").equals(username) && ("123").equals(password) && checkCode.equals(savedCode)) { User user = new User(); user.setUsername(username); user.setPassword(password); request.getSession().setAttribute("user", user); response.sendRedirect("/chapter05/IndexServlet"); } else if (checkCode.equals(savedCode)) { pw.write("用户名或密码错误,登录失败"); } else { pw.write("验证码错误"); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

最新回复(0)