java网络通信:HTTP协议 之 Sessions与Cookies

时间:2021-10-07 20:34:40

  通过前一篇博客的讲解,我们大体知道了HTTP协议是什么,它有什么组成,以及它的工作原理,那么在HTTP的很多特点中,有一点叫做,无状态,就HTTP是一个无状态的协议,如果需要前面的信息用于处理后边的请求,那么在HTTP当中,就需要对前边的信息进行重发,这一点是很不方便的,那么为了解决HTTP在用于需要记录前边信息的场景的问题,提出了这么两个概念,Session和Cookie。那么我们先来了解一下Session是什么呢?

  Session,顾名思义,中文含义是会话,如同前文所述,它是用于解决一类用来在客户端与服务器之间保持状态的解决方案,在java当中讨论的Session,通常指的是javax.servlet.http.HttpSession,可以看出Session它本身是一个对象,那么在这个对象当中主要包含了以下几个要素:1.属性,用于存储Request当中的各类信息。2.SessionID,用于映射相应Request的标识。也许这么说,可能有一些不大清晰,那么我们还是老规矩,举一个栗子来说:

  假设我们的WebServer是一个商场的储物处,而每一个HTTP Request是一个来商场购物的顾客,那么顾客需要在商场当中存包,管理员会将顾客的包放到相应的储物柜,当中而这个储物柜就相当于Session,并且交给顾客相应的号码牌作为顾客离开的时候要取包的凭证,并且之后一段时间这个储物柜就只交给这个顾客使用了,这个号码牌就是SessionID。那么等这个顾客(HTTP Request)下次来的时候,只需要初始相应的号码牌(SessionID),储物处(WEB Server)就会将客户需要用的储物柜(Session)交个顾客使用,顾客可以在储物柜里头放东西(存入相应的属性,setAttribute)以及取东西(getAttribute)等,当然,商场的储物处也可以将顾客的储物柜给取消,然后给其他客户使用。当顾客离开商场的时候(这时候顾客变成了HTTP Response),商场的储物间还会友情的提示储物柜的编号告诉顾客(将SessionID放入Response当中),以防顾客下次忘记带来号码牌。这样顾客下次过来的时候,又会带着相同的号码牌。

  通过这个栗子,我们可以很好的理解了Session与HTTP Request和WEB Server之间的关系咯吧?Request和WEB Server就是依靠Session对该次状态进行记录,并且下次访问的时候,就会通过相同的SessionID获取到上一次的状态,解决了HTTP的无状态的问题,那么我们在标题当中提到的Cookie又是什么东西呢?其实,Cookie就是我们在例子当中提到的 “号码牌”,这个号码牌就放在客户端的(存储与浏览器的)一位顾客不可能一辈子只去一个商场,那么这个顾客手上就会有很多 “号码牌”,当顾客想去其中的某一个商场的时候,就会在自己手里找一找有没有那个商场的号码牌(查找符合作用域的Cookie)然后带着这个号码牌去相应的商场进行存包取包等操作。

  这个例子呢,只是让我们对HTTP Request、WEB Server、Cookie、Session、HTTP Response之间的关系做一个大体的了解,那么接下来,我们开始正经的介绍在HTTP当中的Cookie和Session,以及在面试当中常常遇到过的相关问题。

Cookie 机制

  Cookie在浏览器的生成,主要是通过拓展HTTP协议来实现的,在第一次访问WEB Server的时候,WEB Server会在响应的HTTP Response当中的响应报头添加一行特殊的指示,提示浏览器在本地保存相应的cookie(相当于顾客第一次来商场的储物处存东西,管理员为顾客分配一个储物柜,然后给顾客一个号码牌,要求顾客要拿着)。而这个Cookie的主要内容主要包括以下几点:

1.名字:即cookie的名字。

2.值:在Cookie中存入的值。

3.过期时间:如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

4.路径和域:指定某一个域比如.google.com,也可以指定一个域下的具体某台机器比如www.google.com,路径就是跟在域名后面的URL路径,比如/或者/foo,路径与域合在一起就构成了cookie的作用范围。

这里提到一点,当设置了过期时间的cookie被存在硬盘之后,可以在不同的浏览器进程间共享。对于Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。

Seesion 机制

  session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。当某个客户端发起HTTP Request,并需要使用一个Session的时候,WEB Server首先会检查在这个Http Request里头有没有相应的SessionID,如果已包含一个SessionID则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端的Http Request中不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应Http response中返回给客户端保存。

  这里要格外的提一点,之前的例子当中,一直说SessionID,是用Cookie来进行保存的,这是当然可以的,cookie的名字都是类似于SEEESIONID,但是当客户端(浏览器)禁用Cookie的话,是不是就意味着这个SessionID没法存储了呢?当然是不可能的啊。

  由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid=ByOK ... 99zWpBng!-145788764,另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK ... 99zWpBng!-145788764(这个jsessionid就是用来存储SessionID的),这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

了解完Session和Cookie机制之后,我们来看看一些面试当中的干货。

Session常见问题

1.Session创建的时间:人们常有一个误解就是以为Session是在有客户端访问的时候就一定会被创建,然而事实是到某个Server端程序调用了HttpServletRequest.getSession(true)这样的语句时才被创建,即在打开浏览器第一次请求该jsp的时候,服务器会自动为其创建一个session(JSP没有显示关闭Session的时候),在JSP的默认编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。如果不需要在JSP当中使用Session的话,需要显示的声明关闭Session,<% @page session="false"%>。

注意:访问*.html的静态资源因为不会被编译为Servlet,也就不涉及session的问题、

2.Session删除的时间:满足以下三种情况,则会删除Session。

1)Session超时:超时指的是连续一定时间服务器没有收到该Session所对应客户端的请求,并且这个时间超过了服务器设置的Session超时的最大时间。

2)程序调用HttpSession.invalidate()

3)服务器关闭或服务停止(一般情况下,session是不做持久化的。)

除了以上情况,均不会删除Session,比如关闭浏览器,是不会删除Session的,只会删除会话Cookie(没有被存入硬盘的Cookie)

3.session存放在哪里:存放于服务器的内存当中,也可以做特殊处理进行持久化,存入数据库或者硬盘。

4.SessionID的生成和使用:当客户端第一次请求服务器。且需要应用到Session的时候,服务器会为客户端创建一个Session,并且相应的生成一个SessionID用来表示该Session,当浏览器下次(session继续有效时)请求别的资源的时候,浏览器会自动地将SessionID放置到请求头中,服务器接收到请求后就得到该请求的SessionID,服务器找到该id的Session 返还给请求者(Servlet)使用。一个会话只能有一个Session对象,对Session来说是只认id不认人。

5.同一客户端机器多次请求同一个资源,session是否一样:对于多标签的浏览器(比如360浏览器)来说,在一个浏览器窗口中,多个标签同时访问一个页面,session 是一个。对于多个浏览器窗口之间,同时或者相隔很短时间访问一个页面,session是多个的,和浏览器的进程有关。

总结一下:Session是一个容器,可以存放会话过程中的任何对象,Session因为请求(request对象)而产生,同一个会话中多个request共享了一Session对象,可以直接从请求中获取到Session对象,并且其实,session的创建和使用总在服务端,而浏览器从来都没得到过session对象。但浏览器可以请求Servlet(jsp也是 Servlet)来获取session的信息。客户端浏览器真正紧紧拿到的是session ID,而这个对于浏览器操作的人来说,是不可见的,并且用户也无需关心自己处于哪个会话过程中。

6.Session共享的方法

1)客户端Cookie保存:客户端Cookie保存以cookie加密的方式保存在客户端.

优点是减轻服务器端的压力,每次session信息被写在客服端。然后经浏览器再次提交到服务器。即使两次请求在集群中的两台服务器上完成,也可以到达session共享。

2)服务器间Session同步:使用服务器间session同步使用主-从服务器的架构,当用户在主服务器上登录后,通过脚本或者守护进程的方式,将session信息传递到各个从服务器中,这样用户访问其它的从服务器时,就可以读到session信息。 缺点:比如速度慢、不稳定等,另外,如果 session 信息传递是主->从单向的,会有一些风险,比如主服务器down了,其它服务器无法获得 session 信息

3)使用集群管理Session(如MSM) :使用集群统一管理Session提供一个集群保存session共享信息.其他应用统统把自己的session信息存放到session集群服务器组。当应用系统需要session信息的时候直接到 session 集群服务器上读取。目前大多都是使用 Memcache 来对 Session 进行存储。目前比较流行的两种方案:

a) 使用Filter方式:此方式使用过滤器的方式重新对httpRequest 对象进行了包装,并加入memcached客户端,此方式的优点是:使用简单,把过滤器配置进去即可,另外比较灵活,因为它是在客户端实现的,配置比较灵活,而且服务器无关,你可以在任何支持servlet的容器上部署。
b)使用Memcached-Session-Manager,俗称MSM,是一个用于解决分布式 tomcat 环境下 session 共享的问题的开源解决方案。它的实现原理为以tomcat插件的方式部署在服务器,修改了 servlet 容器代码中的 session 相关代码,使其连接 memcached ,在 memcached 中创建和更新session。

4)把Session持久化到数据库:将session持久化到数据中这种共享session的方式即将session信息存入数据库中,其它应用可以从数据库中查出session信息。目前采用这种方案时所使用的数据库一般为mysql。 利用数据库共享 session 的方案有一定的实用性,但也有如下缺点:首先 session 的并发读写在数据库中完成,对 mysql 的性能要求比较高;其次,我们需要额外地实现 session 淘汰(超时)逻辑代码,即定时从数据库表中更新和删除 session 信息,增加了工作量。