大家好,我是来自郑州分院的第10期java学员,今天我要讲session和cookie,例行公事,先来背景介绍
背景介绍
Cookie意为“甜饼”,是cookie 最早是网景公司的雇员 Lou Montulli 在1993年3月发明,后被 W3C 采纳,最早由Netscape社区发展的一种机制。目 前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。
知识剖析
要求网站所要有一个对事务处理的记忆功能,事务处理的记忆功能就是我们常说的要有状态。而实现web应用技术的核心http协议是一个无状态的协议。
因此在web应用开发里就出现了保持http链接状态的技术:一个是cookie技术,另一种是session技术。
cookie技术是客户端的解决方案,Cookie就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。说得更具体一些:当用户使用浏览器访问一个支持Cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,这些信息存放于HTTP响应头(Response Header);当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置;自此,客户端再向服务器发送请求的时候,都会把相应的Cookie再次发回至服务器。这时,Cookie信息存放在HTTP请求头(Request Header)了。有了Cookie这样的技术实现,服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的Cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。通常,我们可以从很多网站的登录界面中看到“请记住我”这样的选项,如果你勾选了它之后再登录,那么在下一次访问该网站的时候就不需要进行重复而繁琐的登录动作了,而这个功能就是通过Cookie实现的。
session技术则是服务端的解决方案,它是通过服务器来保持状态的。由于Session这个词汇包含的语义很多,因此需要在这里明确一下 Session的含义。首先,我们通常都会把Session翻译成会话,因此我们可以把客户端浏览器与服务器之间一系列交互的动作称为一个 Session。从这个语义出发,我们会提到Session持续的时间,会提到在Session过程中进行了什么操作等等;其次,Session指的是服务器端为客户端所开辟的存储空间,在其中保存的信息就是用于保持状态。从这个语义出发,我们则会提到往Session中存放什么内容,如何根据键值从 Session中获取匹配的内容等。要使用Session,第一步当然是创建Session了。那么Session在何时创建呢?当然还是在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建Session的方法,而在Java中是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建的。在创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有Session id;当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。正式这样一个过程,用户的状态也就得以保持了。
常见问题
cookie和session的使用。
String name:该Cookie的名称。Cookie一旦创建,名称便不可更改。
Object value:该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码。
int maxAge:该Cookie失效的时间,单位秒。如果为正数,则该Cookie在>maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1。
boolean secure:该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络>上传输数据之前先将数据加密。默认为false。
String 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规范。
HttpOnly为true,通过js脚本无法读取cookie信息,可以防止XSS攻击。需要注意的一点是Servlet2.5不支持cookie设置HttpOnly,Servlet3.0及以上就可以。
1、getCreationTime
public long getCreationTime();
返回建立 session 的时间,这个时间表示为自 1970-1-1 日(GMT)以来的毫秒数。
2、getId
public String getId();
返回分配给这个 session 的标识符。一个 HTTP session 的标识符是一个由服务器来建立和维持的唯一的字符串。
3、getLastAccessedTime
public long getLastAccessedTime();
返回客户端最后一次发出与这个 session 有关的请求的时间, 如果这个 session 是新建立的,返回-1。这个时间表示为自 1970-1-1 日(GMT)以来的毫秒数。
4、getMaxInactiveInterval
public int getMaxInactiveInterval();
返加一个秒数,这个秒数表示客户端在不发出请求时,session 被 Servlet 引擎维持的最长时间。在这个时间之后,Servlet 引擎可能被 Servlet 引擎终止。如果这个 session 不会被终
止,这个方法返回-1。当 session 无效后再调用这个方法会抛出一个 IllegalStateException。
5、getValue
public Object getValue(String name);
返回一个以给定的名字绑定到 session 上的对象。如果不存在这样的绑定,返回空值。当 session 无效后再调用这个方法会抛出一个 IllegalStateException。
6、getValueNames
public String[] getValueNames();
以一个数组返回绑定到 session 上的所有数据的名称。当 session 无效后再调用这个方法会抛出一个 IllegalStateException。
7、invalidate
public void invalidate();
这个方法会终止这个 session。所有绑定在这个 session 上的数据都会被清除。并通过HttpSessionBindingListener 接口的 valueUnbound 方法发出通告。
8、isNew
public boolean isNew();
返回一个布尔值以判断这个 session 是不是新的。 如果一个 session 已经被服务器建立但是还没有收到相应的客户端的请求,这个 session 将被 认为是新的。这意味着,这个客户端
还没有加入会话或没有被会话公认。在他发出下一个请求时还不能返回适当的 session 认证信息。当 session 无效后再调用这个方法会抛出一个 IllegalStateException。
9、putValue
public void putValue(String name, Object value);
以给定的名字,绑定给定的对象到 session 中。已存在的同名的绑定会被重置。这时会调用 HttpSessionBindingListener 接口的 valueBound 方法。当 session 无效后再调用这个方法会抛出一个 IllegalStateException。
10、removeValue
public void removeValue(String name);
取消给定名字的对象在 session 上的绑定。如果未找到给定名字的绑定的对象,这个方法什么出不做。 这时会调用 HttpSessionBindingListener 接口的 valueUnbound 方法。当 session 无效后再调用这个方法会抛出一个 IllegalStateException。
11、setMaxInactiveInterval
public int setMaxInactiveInterval(int interval);
设置一个秒数,这个秒数表示客户端在不发出请求时,session 被 Servlet 引擎维持的最长时间。以下这个方法将被取消\
12、getSessionContext
public HttpSessionContextgetSessionContext();
返回 session 在其中得以保持的环境变量。 这个方法和其他所有 HttpSessionContext 的方法一样被取消了。
扩展思考
Cookie跨域访问和Session在集群下共享
跨域是指a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,或是a页面为ip地址,b页面为域名地址,所进行的访问行动都是跨域的。
常见解决办法:
1.jsonp 需要目标服务器配合一个callback函数。
2.window.name+iframe 需要目标服务器响应window.name。
3.window.location.hash+iframe 同样需要目标服务器作处理。
4.html5的 postMessage+ifrme 这个也是需要目标服务器或者说是目标页面写一个postMessage,主要侧重于前端通讯。
5.CORS 需要服务器设置header :Access-Control-Allow-Origin。
6.nginx反向代理
session是保存在每台服务器的,但在集群中,拥有多台服务器,每台各自独立,由于session的不同步会造成很多问题。
解决方案:
1.持久化session到数据库,即使用数据库来储存session。数据库正好是我们普遍使用的公共储存空间,一举两得,推荐使用mysql数据库,轻量并且性能良好。
优点:就地取材,符合大多数人的思维,使用简单,不需要太多额外编码工作
缺点:对mysql性能要求较高,访问mysql需要从连接池中获取连接,又因为大部分请求均需要进行登录鉴权,所以操作数据库非常频繁,当用户量达到一定程度之后,极易造成数据库瓶颈,不适用于处理高并发的情况。
2.使用redis共享session。redis是一个key-value的储存系统。可以简单的将其理解为一个数据库,与传统数据库的区别是,它将数据储存于内存中,并自带有内存到硬盘的序列化策略,即按策略将内存中的数据同步到磁盘,避免数据丢失,是目前比较流行的解决方案。
优点:无需增加数据库的压力,因为数据存储于内存中,所以读取非常快,高性能,并能处理多种类型的数据。
缺点:额外增加一些编码,以便操作redis。
3.使用memcache同步session,memcache可以实现分布式,可将服务器中的内存组合起来,形成一个“内存池”,以此充当公共空间,保存session信息。
优点:数据储存在内存中,读取非常快,性能好;
缺点:memcache把内存分成很多种规格的存储块,有大有小,不能完全利用内存,会产生内存碎片,浪费资源,如果储存块不足,还会产生内存溢出。
4.通过脚本或守护进程在多台服务器之间同步session。
优点:实现了session共享;
缺点:对个人来说实现较为复杂,速度不稳定,有延时性,取决于现实中服务运行状态,偶然性较大,如果用于访问过快,可能出现session还没同步成功的情况。
5.使用NFS共享session。NFS是Network File Server共享服务器的简称,最早由Sun公司为解决Unix网络主机间的目录共享而研发。选择一台公共的NFS做共享服务器,储存所有session数据,每台服务器所需的session均从此处获取。
优点:较好的实现了session共享;
缺点:成本较高,对于个人来说难以实现。NFS依托于复杂的安全机制和文件系统,因此并发效率不高。
6.使用Cookie共享session。此方案可以说是独辟蹊径了,将分布式思想用到了极致。如上文分析所说,session-cookie机制中,session与cookie相互关联,以cookie做中转站,用来找到对应的session,其中session存放在服务器。那么如果将session中的内容存放在cookie中呢,那么则省略了服务器保存session的过程,后台只需要根据cookie中约定的标识进行鉴权校验即可。
优点:完美的贯彻分布式的理念,将每个用户都利用起来,无需耗费额外的服务器资源;
缺点:受http协议头长度限制,cookie中存储的信息不宜过多;为了保持cookie全局有效,所以其一般依赖在根域名下,所以基本上所有的http请求都需要传递cookie中的这些标记信息,所以会占用一些服务器的带宽;鉴权信息全存储于cookie中,cookie存在于客户端,服务器并没有储存相关信息,cookie存在着泄露的可能,或则其他人揣摩出规则后可以进行伪装,其安全性比其他方案差,故需要对cookie中信息进行加密解密,来增强其安全性。
对于我们来说,比较合适的处理方案就是把session存入redis或memcached。
基本有两种思路:
1、利用Servlet容器提供的插件功能,自定义HttpSession的创建和管理策略,并通过配置的方式替换掉默认的策略。这方面其实早就有开源项目了,例如memcached-session-manager(可以参考负载均衡+session共享(memcached-session-manager实现),以及tomcat-redis-session-manager。不过这种方式有个缺点,就是需要耦合Tomcat/Jetty等Servlet容器的代码。
2、设计一个Filter,利用HttpServletRequestWrapper,实现自己的 getSession()方法,接管创建和管理Session数据的工作。
参考文献
https://blog.csdn.net/zoubo0812/article/details/53997744
https://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#httpsession-redis
更多讨论
Q1:cookie 和session 的区别:
A1:1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
Q2:如何应对客户端禁用Cookies?
A2:对所有的URL使用URL重写,包括超链接,form的action,和重定向的URL
Q3:存放在session中的对象必须是可序列化的吗?
A3:不是必须的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。