会话管理相关

会话管理

会话管理概述
在人机交互时,会话管理是保持用户的整个会话活动的互动与计算机系统跟踪过程。会话管理分类:桌面会话管理、浏览器会话管理、Web服务器的会话管理。

为什么需要会话管理
HTTP是一种无状态协议,一次请求结束,客户端与服务端的连接就会断开,服务器再次收到请求时,无法识别此次请求是哪个用户发过来的,需要重新建立连接。为了判断发送请求的用户,需要一种记录用户的方式,也就是Web应用会话管理。

什么是会话?
(1) 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。

(2) 一次会话:打开浏览器 -> 访问一些服务器内容 -> 关闭浏览器

会话过程中要解决的一些问题
每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服务器要想办法为每个用户保存这些数据。

(1) 模拟登陆场景

① 过程:打开浏览器 -> 浏览到登陆页面 -> 输入用户名和密码 -> 访问到用户主页(显示用户名)
修改密码(输入原密码)
修改收货地址

②存在的问题:在此处登录会话过程中产生的数据(用户会话数据)如何保存下来呢?

(2) 模拟购物场景

①过程:

打开浏览器 -> 浏览商品列表 -> 加入购物车(把商品信息保存下来) -> 关闭浏览器
打开浏览器 -> 直接进入购物车 -> 查看到上次加入购物车的商品 -> 下订单 -> 支付

②存在的问题:在购物会话过程中,如何保存商品信息?

常见的Web应用会话管理的方式

基于server端:session的管理方式
cookie-based的管理方式
token-based的管理方式

基于server端session的管理的方式

在早期的Web应用中,通常使用服务端session来管理用户的会话。
服务端session是用户第一次访问应用时,服务器就会创建的对象,代表用户的一次会话过程,可以用来存放数据。服务器为每一个session都分配一个唯一的session ID,以保证每个用户都有一个不同的session对象。
服务器在创建完session后,会把session ID通过cookie返回给用户所在的浏览器,这样当用户第二次及以后向服务器发送请求的时候,就会通过cookie把session ID传回给服务器,以便服务器能够根据session ID找到与该用户对应的session对象。
session通常设定有有效时间,比如1个小时。当时间失效后,服务器会销毁之前的session,并创建新的session返回给用户。但是只要用户在失效时间内,有发送新的请求给服务器,通常服务器都会把他对应的session的有效时间根据当前的请求时间再重新刷新。
session在一开始并不具备会话管理的作用。它只有在用户登录认证成功之后,并且往session对象里面放入了用户登录成功的凭证,才能用来管理会话。管理会话的逻辑也很简单,只要拿到用户的session对象,看它里面有没有登录成功的凭证,就能判断这个用户是否已经登录。当用户主动退出的时候,会把它的session对象里的登录凭证清掉。所以在用户登录前或退出后或者session对象失效时,肯定都是拿不到需要的登录凭证的。

优点

某些地方使用可以简化Web开发:如果在诸多Web页面间传递一个变量,那么用session变量要比通过QueryString传递变量可使问题简化。
安全性好:客户端与服务端保持会话状态的媒介始终只是一个session ID串,只要这个串够随机,攻击者就不能轻易冒充他人的session ID进行操作;除非通过CSRF或http劫持的方式,才有可能冒充别人进行操作;即使冒充成功,也必须被冒充的用户session里面包含有效的登录凭证才行。
缺点

这种方式将会话信息存储在Web服务器里面,当用户同时在线量比较多时,这些会话信息会占据比较多的内存;
当应用采用集群部署的时候,会遇到多台web服务器之间如何做session共享的问题。
多个应用要共享session时,还会遇到跨域问题。不同的应用可能部署的主机不样,需要在各个应用做好cookie跨域的处理。

在这里插入图片描述

在这里插入图片描述

Session技术核心
HttpSession类:用于保存会话数据。

(1) 创建或得到session对象

① HttpSession getSession()
② HttpSession getSession(boolean create)

(2) 设置session对象

① void setMaxInactiveInterval(int interval):设置session的有效时间
② void invalidate():销毁session对象
③ java.lang.String getId():得到session编号

(3) 保存会话数据到session对象

① void setAttribute(java.lang.String name, java.lang.Object value):保存数据
② java.lang.Object getAttribute(java.lang.String name):获取数据
③ void removeAttribute(java.lang.String name):清除数据

Session实例
(1) 实例一:保存会话数据到Session域对象和从Session域对象中取出会话数据。


/*
 * 保存会话数据到session域对象
 */
@WebServlet("/SessionDemo1")
public class SessionDemo1 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 1.创建session对象
		HttpSession session = request.getSession();
		// 2.保存会话数据
		session.setAttribute("name", "rose");
		System.out.println("保存成功");
	}
}


/*
 * 从session域对象中取出会话数据。
 */
@WebServlet("/SessionDemo2")
public class SessionDemo2 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 1.得到session对象
		HttpSession session = request.getSession();
		// 2.取出数据
		String name = (String) session.getAttribute("name");
		System.out.println("name=" + name);
	}
}

实例二:避免cookie随着浏览器的关闭而丢失。

@WebServlet("/SessionDemo3")
public class SessionDemo3 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 1.创建session对象
		HttpSession session = request.getSession();
		/*
		 * 得到session编号
		 */
		System.out.println("id=" + session.getId());
		// 2.保存会话数据
		session.setAttribute("name", "rose");
		/*
		 * 手动发送一个硬盘保存的cookie数据给浏览器,这样就可以避免cookie随着浏览器的关闭而丢失的问题。
		 */
		Cookie c = new Cookie("JSESSIONID", session.getId());
		c.setMaxAge(60 * 60);
		response.addCookie(c);
	}
}



@WebServlet("/SessionDemo4")
public class SessionDemo4 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 1.得到session对象
		HttpSession session = request.getSession();
		/*
		 * 得到session编号
		 */
		System.out.println("id=" + session.getId());
		// 2.取出数据
		String name = (String) session.getAttribute("name");
		System.out.println("name=" + name);
	}
}


实例三:销毁Session对象。

/*
 * 销毁session对象
 */
@WebServlet("/DeleteSession")
public class DeleteSession extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		HttpSession session = request.getSession(false);// 如果里面传入false,那么必须判定它不为空
		if (session != null) {
			session.invalidate();// 手动销毁
		}
		System.out.println("销毁成功");
	}
}

Cookie-based的管理方式

session的管理方式会增加服务器的负担和架构的复杂性,所以后来就提出把用户的登录凭证直接存到客户端的方案,当用户登录成功之后,把登录凭证写到cookie里面,并给cookie设置有效期,后续请求直接验证存有登录凭证的cookie是否存在以及凭证是否有效,即可判断用户的登录状态。
用户发起登录请求,服务端根据传入的用户密码之类的身份信息,验证用户是否满足登录条件,如果满足,就根据用户信息创建一个登录凭证,这个登录凭证简单来说就是一个对象,最简单的形式可以只包含用户id、凭证创建时间和过期时间三个值。
服务端把上一步创建好的登录凭证,先对它做数字签名,然后再用对称加密算法做加密处理,将签名、加密后的字串,写入cookie。cookie的名字必须固定(如ticket),因为后面再获取的时候,还得根据这个名字来获取cookie值。这一步添加数字签名的目的是防止登录凭证里的信息被篡改,因为一旦信息被篡改,那么下一步做签名验证的时候肯定会失败。做加密的目的,是防止cookie被别人截取的时候,无法轻易读到其中的用户信息。
用户登录后发起后续请求,服务端根据上一步存登录凭证的cookie名字,获取到相关的cookie值。然后先做解密处理,再做数字签名的认证,如果这两步都失败,说明这个登录凭证非法;如果这两步成功,接着就可以拿到原始存入的登录凭证了。然后用这个凭证的过期时间和当前时间做对比,判断凭证是否过期,如果过期,就需要用户再重新登录;如果未过期,则允许请求继续。

优点
实现了服务端的无状态化(最大的优点), 服务端只需要负责创建和验证登录cookie即可,无需保持用户的状态信息。
cookie可以跨越同域名下的的多个网页,但不能跨越多个域名使用
可以设置有效期限,控制cookie的生命周期,使之不会永远有效(攻击者可能拿到的是过期的cookie)

缺点
cookie有大小限制,存储不了太多数据。
每次传送cookie,增加了请求的数量,对访问性能也有影响。
同样存在跨域问题(不同域名无法互相读取cookie)

在这里插入图片描述

Cookie技术核心
Cookie类:用于存储会话数据。代码要在服务器端完成,服务器是运行这些代码的,我们在服务器只是把cookie创建出来,但是服务器并不保存数据,而是把cookie数据发送到浏览器端去保存。而代码要交给服务器运行,那
么这个代码可以写到Servlet中去,Servlet可以通过浏览器访问运行。
javax.servlet.http.Cookie类用于创建一个Cookie,response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。

(1) 构造Cookie对象

Cookie(java.lang.String name, java.lang.String value); ----我们需要通过名字来获取对应的值。

(2) 设置cookie

① void setPath(java.lang.String uri); ----设置cookie的有效访问路径
② void setMaxAge(int expiry); ----设置cookie的有效时间
③ void setValue(java.lang.String newValue); ----设置cookie的值

(3) 发送cookie到浏览器端保存

void response.addCookie(Cookie cookie); ----发送cookie

(4) 浏览器是可以把cookie再发送回来的,服务器接收cookie

Cookie[] request.getCookies(); ----接收cookie

Cookie原理
(1) 服务器创建cookie对象,把会话数据存储到cookie对象中。

new Cookie(“name”,“value”);

(2) 服务器发送cookie信息到浏览器

response.addCookie(cookie);
举例: set-cookie: name=eric (隐藏发送了一个set-cookie名称的响应头)

(3) 浏览器得到服务器发送的cookie,然后保存在浏览器端。

(4) 浏览器在下次访问服务器时,会带着cookie信息

举例:cookie: name=eric (隐藏带着一个叫cookie名称的请求头)

(5) 服务器接收到浏览器带来的cookie信息

request.getCookies();

Cookie的细节
(1) void setPath(java.lang.String uri):设置cookie的有效访问路径。有效路径指的是cookie的有效路径保存在哪里,那么浏览器在有效路径下访问服务器时就会带着cookie信息,否则不带cookie信息。

(2) void setMaxAge(int expiry):设置cookie的有效时间。

① 正整数:表示cookie数据保存浏览器的缓存目录(硬盘中),数值表示保存的时间。
② 负整数:表示cookie数据保存浏览器的内存中。浏览器关闭cookie就丢失了!!
③ 零:表示删除同名的cookie数据。

(3) 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。

(4) 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。

(5) 如果创建了一个cookie,并将它发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。

(6) 注意,删除cookie时,path必须一致,否则不会删除。

(7) Cookie数据类型只能保存非中文字符串类型的。可以保存多个cookie,但是浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

Cookie的实例
实例一:发送与接收Cookie

@WebServlet("/CookieDemo1")
public class CookieDemo1 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 1.创建Cookie对象
		Cookie cookie = new Cookie("name", "value");
		Cookie cookie1 = new Cookie("email", "eric@qq.com");
		// 2.把cookie数据发送到浏览器(cookie数据也需要通过响应才能发送出去)
		// 但是cookie数据要跟在响应的哪一部分呢?可以通过响应头(set-cookie名称)发送。
		// response.setHeader("set-cookie",cookie.getName()+"="+cookie.getValue());
		// 推荐使用这种方式,可以避免手动发送cookie信息
		response.addCookie(cookie);
		response.addCookie(cookie1);
 
		// 3.接收浏览器发送的cookie信息
		/*
		 * String name = request.getHeader("cookie"); System.out.println(name);
		 */
		Cookie[] cookies = request.getCookies();
		// 注意:要判断是否为空,如果不判断,可能空指针
		if (cookies != null) {
			// 遍历
			for (Cookie c : cookies) {
				String name = c.getName();
				String value = c.getValue();
				System.out.println(name + "=" + value);
			}
		} else {
			System.out.println("没有接收到cookie数据");
		}
	}
}

实例二:设置Cookie的有效路径

@WebServlet("/CookieDemo2")
public class CookieDemo2 extends HttpServlet {
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
   		throws ServletException, IOException {
   	// 1.创建Cookie对象
   	Cookie cookie = new Cookie("name", "eric");
   	Cookie cookie1 = new Cookie("email", "eric@qq.com");
   	/*
   	 * (1)设置cookie的有效路径,默认情况下,有效路径在当前web应用下/day11
   	 */
   	cookie.setPath("/day11");
   	cookie1.setPath("/day12");
   	// 2.把cookie数据发送到浏览器(cookie数据也需要通过响应才能发送出去)
   	// 但是cookie数据要跟在响应的哪一部分呢?可以通过响应头(set-cookie名称)发送。
   	// response.setHeader("set-cookie",
   	// cookie.getName()+"="+cookie.getValue());
   	// 推荐使用这种方式,可以避免手动发送cookie信息
   	response.addCookie(cookie);
   	response.addCookie(cookie1);
   	// 3.接收浏览器发送的cookie信息
   	/*
   	 * String name = request.getHeader("cookie"); System.out.println(name);
   	 */
   	Cookie[] cookies = request.getCookies();
   	// 注意:要判断是否为空,如果不判断,可能空指针
   	if (cookies != null) {
   		// 遍历
   		for (Cookie c : cookies) {
   			String name = c.getName();
   			String value = c.getValue();
   			System.out.println(name + "=" + value);
   		}
   	} else {
   		System.out.println("没有接收到cookie数据");
   	}
   }
}

实例三:设置Cookie的有效时间

/*
 * 设置Cookie的有效时间
 */
@WebServlet("/CookieDemo3")
public class CookieDemo3 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 1.创建Cookie对象
		Cookie cookie = new Cookie("name", "eric");
		/*
		 * (2) 设置Cookie的有效时间
		 */
		//cookie.setMaxAge(20);//20秒,意味着Cookie是20秒之后过期。从最后不调用Cookie开始计算。
		cookie.setMaxAge(-1);//cookie会保存在浏览器内存(会话Cookie)。
 
		// 2.把cookie数据发送到浏览器(cookie数据也需要通过响应才能发送出去)
		// 但是cookie数据要跟在响应的哪一部分呢?可以通过响应头(set-cookie名称)发送。
		// response.setHeader("set-cookie",
		// cookie.getName()+"="+cookie.getValue());
		// 推荐使用这种方式,可以避免手动发送cookie信息
		response.addCookie(cookie);
		// 3.接收浏览器发送的cookie信息
		/*
		 * String name = request.getHeader("cookie"); System.out.println(name);
		 */
		Cookie[] cookies = request.getCookies();
		// 注意:要判断是否为空,如果不判断,可能空指针
		if (cookies != null) {
			// 遍历
			for (Cookie c : cookies) {
				String name = c.getName();
				String value = c.getValue();
				System.out.println(name + "=" + value);
			}
		} else {
			System.out.println("没有接收到cookie数据");
		}
	}
}

实例四:删除Cookie


@WebServlet("/DeleteCookie")
public class DeleteCookie extends HttpServlet {
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
   		throws ServletException, IOException {
   	Cookie cookie = new Cookie("name", "xxx");
   	cookie.setMaxAge(0);// 删除同名的Cookie
   	response.addCookie(cookie);
   	System.out.println("删除成功");
   }
}

token-based的管理方式
Session和Cookie两种会话管理方式由于都要用到Cookie,不适合用在nativeapp里面,因为native app不是浏览器,不好管理Cookie,因此都不适合做纯API服务的登录认证。要实现API服务的登录认证,就需要用到token-based的会话管理方式。
token-based的管理方式从流程上和实现上跟cookie-based的管理方式没有太多区别,只不过cookie-based的管理方式中写到cookie里面的ticket在这种方式下称为token,这个token在返回给客户端之后,后续请求都必须通过url参数或者是httpheader的形式,主动带上token,这样服务端接收到请求之后就能直接从http header或者url里面取到token进行验证。

优点
支持跨域访问:Cookie是不支持跨域访问的,Token支持无状态:Token无状态,Session有状态(有状态和无状态最大的区别就是服务端会不会保存客户端的信息)支持移动设备:Token更适用于移动应用,Cookie不支持手机端访问

缺点
占带宽:正常情况下Token要比session ID更大,需要消耗更多的流量,挤占更多带宽无法在服务端注销,很难解决劫持问题

在这里插入图片描述

安全问题
在Web应用里,会话管理的安全性始终是最重要的安全问题,对用户的影响极大。
从会话管理凭证来说,Session会话管理的会话凭证仅仅是一个session ID,所以只要这个session ID足够随机,那么攻击者就不会轻易地冒充别人的session ID进行操作;Cookie会话管理的凭证(ticket)以及Token会话管理凭证(token)都是一个在服务端做了数字签名和加密处理的串,所以只要密钥不泄露,攻击者也无法轻易拿到这个串中有效信息并对它进行篡改。总之,这三种会话管理方式的凭证本身是比较安全的。
从客户端和服务端的HTTP过程来说,当攻击者截获到客户端请求中的会话凭证,就能拿这个凭证冒充原用户,做一些非法操作,而服务器也认不出来。这种安全问题,可以简单采用HTTPS来解决,虽然可能还有HTTP劫持这种更高程度的威胁存在,但是从代码能做的防范,确实也就是这个层次了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值