Sessions and Cookies
1. What
1.1 Session定义
Session对象是存储在服务器端的,主要用来存储用户会话所需的属性数据和配置数据。SessionlD需要存储在浏览器端,浏览器发送接口请求的时候需要带着这个SessionlD。一个会话就是用户通过浏览器与服务器之间进行的一次通话。
session的工作原理:
- 服务器程序运行的过程中产生session,并且为该session生成唯一的session ID
- 服务器将session ID发送给客户端
- 客户端再次发送请求时,会将这个session ID带上
- 服务器接收到请求之后,根据session ID找到相应的session,完成请求响应。
Session失效:
- 服务器会把长时间没有活动的Session从服务器内存中清除,此时Session便失效。Tomcat中Session的默认失效时间为20分钟。
- 调用Session的invalidate方法。
1.2 Cookie定义
Cookie是一小段存储在浏览器端的文本数据,大小不超过4KB。
key-value的数据:
name:cookie的名称
value:cookie的值
max-age:生存周期,max-age用秒来设置cookie的生存期
- max-age属性为正数,则表示该cookie会在max-age秒之后自动失效
- max-age为负数,则表示该cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该cookie即失效。
- max-age为0,则表示删除该cookie
domain :Cookie 所属的域
path:Cookie 来源的网址路径
secure: 该Cookie是否仅被使用于安全协议传输。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。
httponly: 指示 Cookie 应仅由浏览器在 HTTP 请求中设置,而无法通过 JavaScript 访问。
2. Why
HTTP协议是无状态的,每次浏览器向服务器请求时,服务器都会视为新的请求,因此我们需要会话跟踪技术来实现会话内数据共享。
3. How
Cookie和Session的比较:
比较因素 | Cookies | Session |
---|---|---|
存储方式 | Cookie只能存储字符串,如果非要存储ASCII字符串还要对其编码 | Session可以存储任何类型的数据,可以把Session看出一个容器 |
隐私安全 | Cookie存储在浏览器中,对客户端是可见的。信息容易泄露出去。如果使用Cookie,最好将Cookie加密 | Session存储在服务器上,对客户端是透明的。不存在敏感信息泄露问题 |
有效期 | Cookie保存在硬盘中,只需要设置magAge属性来控制,即使关闭浏览器,Cookie还是存在的 | Session保存在服务器中,设置maxlnactiveInterval属性来确定有效期。(如果使用Cookie的方式跟踪会话,Session依赖JessionID的Cookie,如果设置该Cookie的maxAge为-1,当关闭浏览器,该Session虽然没有从服务器中消亡,但是也还是失效的) |
服务器的负担 | Cookie保存在客户端,不占用服务器资源。(baidu,Sina这样的大型网站一般使用Cookie来进行会话跟踪 | Session保存在服务器中,每个用户都会产生一个Session,如果并发访问的用户非常多,Session会消耗大量的内存 |
有浏览器支持 | 如果浏览器禁用了Cookie,那Cookie就无用了 | 如果浏览器禁用了Cookie,Session可以通过url地址重写进行会话跟踪 |
跨域名 | Cookie可以设置Domain属性来实现跨域名 | Session只在当前的域名内有效,不可跨域名 |
储存大小 | Cookie最多可以放4K大小的内容 | Session大小没有限制 |
4. Samples
4.1 Cookie的基本使用
- 发送Cookie
创建Cookie对象,设置数据: Cookie cookie =new Cookie(“key”,“value”);
发送到Cookie客户端:使用response对象:response.addCookie(cookie); - 获取Cookie
获取客户端携带的所有Cookie,使用request对象:Cookie[] cookie =request.getCookies();
遍历数组,获取每一个Cookie对象:for
使用Cookie对象方法获取数据 cookie.getName(); - Cookie存储中文
Cookie默认不能存储中文,如需要存储,可以进行URL转码
4.2 Session的基本使用
- 获取Session对象:HttpSession session =request.getSession();
- 存储Session对象功能
void setAttribute(String name,Object o): 存储数据到session域中
Object getAttribute(String name ): 根据key,获取值
void removeAttribute(String name): 根据key,删除该键值对
4.3 实现会话跟踪的技术
4.3.1 Cookie
Cookie 是信息的键值对,由服务器发送到浏览器,然后浏览器将这个标识符连同那里的每个请求一起发送回服务器。
缺点:用户可以在浏览器中禁用 cookie 支持,在这种情况下服务器将无法识别用户,因此这是这种方法的主要缺点。
4.3.2 隐藏表单域
信息通过隐藏的表单字段插入网页,然后传输到服务器。这些字段对用户的视图是隐藏的。例如
<input type=”hidden” name=”sessionId” value=”unique value”/>
缺点:由于我们不能硬编码为会话跟踪目的而创建的隐藏字段的值,这意味着我们不能将这种方法用于HTML等静态页面
4.3.3 URL 重写
URL重写是在每个请求 URL 中附加一个会话(唯一)标识符以便服务器可以识别用户会话的方法。
例如:
如果我们在 http://localhost:8080/HelloWorld/SourceServlet 上应用URL重写,它将变成类似
http://localhost:8080/HelloWorld/SourceServlet?jSessionId=XYZ
其中 jSessionId=XYZ 是附加的会话标识符,服务器将使用值 XYZ 来识别用户会话。
优点:独立于浏览器,即使浏览器不支持Cookie,也可以实现功能
缺点:我们需要重新生成每个url以附加会话标识符,并且需要跟踪该标识符直到会话完成。
4.3.4 会话跟踪API
Servlet 使用 HttpSession API提供了一个方便且稳定的会话跟踪解决方案。
servlet 中的会话跟踪非常简单,包括以下步骤:
- 使用request.getSession()获取关联的会话对象 (HttpSession ) 。
- 要从会话对象中获取特定值,请在 HttpSession 对象上调用getAttribute(String) 。
- 将任何信息存储在会话对象上的会话调用 setAttribute(key,object) 中。
- 要删除会话数据,请调用 removeAttribute(key) 以丢弃具有给定键的对象。
- 要使会话无效,请在会话对象上调用 invalidate()。这用于注销已登录的用户。
4.4 利用Session防止表单重复提交
服务器返回表单页面时,会先生成一个subToken保存于session,并把该subToen传给表单页面。当表单提交时会带上subToken,服务器拦截器Interceptor会拦截该请求,拦截器判断session保存的subToken和表单提交subToken是否一致。若不一致或session的subToken为空或表单未携带subToken则不通过。
首次提交表单时session的subToken与表单携带的subToken一致走正常流程,然后拦截器内会删除session保存的subToken。当再次提交表单时由于session的subToken为空则不通过。从而实现了防止表单重复提交。
4.5 微服务分布式session共享解决方案
4.5.1 基于tomcat的session共享(通常不采用)
优点:不需要额外开发,只需要搭建tomcat集权即可
缺点:tomcat是全局session复制,集群内每个tomcat的session完全同步,在大规模应用的时候,用户过多,集群内tomcat数量过多,session的全局复制会导致集群性能下降,而且依赖tomcat容器移植性不好。
4.5.2 cookie同步session,如JWT(json web token)
这种方式就是把客户信息保存在客户端的cookie中,每次请求带着cookie中的token
优点:由于完全舍弃了session,会减轻服务器端的压力
缺点:把客户信息暴露在外,存在安全性问题,当浏览器禁用cookie的情况下无效
4.5.3 redis集中管理session(常用)
优点:redis 为内存数据库,读写效率高,并可在集群环境下做高可用
4.5.4 基于Nginx的ip_hash负载均衡
其实就是对请求过来的ip地址对你的多少台可用的服务器进行取模,然后就会把你的请求通过Nginx的反向代理给分发到对应的服务器上。(这里会把可用的服务器放到一个数组中,如果取模得到的结果是几,就把请求分到服务器数组中的下标为几的服务器上)。
优点:配置简单,不需要修改代码。便于服务器水平扩展。安全性较高。
缺点:服务器重启会造成部分session丢失。水平扩展也会造成session丢失。存在单点负载较高的风险。