【Java Web编程 九】深入理解会话追踪技术Session和Cookie(下)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 【Java Web编程 九】深入理解会话追踪技术Session和Cookie

Session基本概念

服务器第一次接收到请求时,开辟了一块 Session 空间(创建了Session对象),同时生成一个 sessionId ,并通过响应头的 Set-Cookie:JSESSIONID=XXXXXXX 命令,向客户端发送要求设置 Cookie 的响应; 客户端收到响应后,在本机客户端设置了一个 JSESSIONID=XXXXXXX 的 Cookie 信息,该 Cookie 的过期时间为浏览器会话结束

接下来客户端每次向同一个网站发送请求时,请求头都会带上该 Cookie信息(包含 sessionId ), 然后,服务器通过读取请求头中的 Cookie 信息,获取名称为 JSESSIONID 的值,得到此次请求的 sessionId。

Session生命周期

Session存储在服务器端,一般放置在服务器的内存中(为了高速存取),Session在用户访问第一次访问服务器时创建,需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session,可调用request.getSession(true)强制生成Session。Session什么时候失效?

  1. 服务器会把长时间没有活动的Session从服务器内存中清除,此时Session便失效。Tomcat中Session的默认失效时间为30分钟。
  2. 调用Session的invalidate方法。

Session对浏览器的要求:虽然Session保存在服务器,对客户端是透明的,它的正常运行仍然需要客户端浏览器的支持。这是因为Session需要使用Cookie作为识别标志。HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一客户,因此服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie中的JSESSIONID来识别是否为同一用户

该Cookie为服务器自动生成的,它的maxAge属性一般为-1,表示仅当前浏览器内有效,并且各浏览器窗口间不共享,关闭浏览器就会失效。

为什么session只能显示在一个浏览器,根源就在cookie,关闭浏览器的时候,会话cookie被关闭,sessionid被丢失,再次访问的时候由于找不到sessionid只能创建新的sessionid,因此同一机器的两个浏览器窗口访问服务器时,会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口除外。这类子窗口(在链接上右击,在弹出的快捷菜单中选择在新窗口中打"时,子窗口便可以访问父窗口的Session)会共享父窗口的Cookie,因此会共享一个Session,以下是一个子窗口的示例:

所以说Session并没有被销毁,只是我们打开另一个浏览器使用的是一个新的session,我们用新的session而不用旧的,相当于抛弃了它,实际上它还默默的在服务器端活30分钟。相反request和response每次请求后确确实实被销毁了。

Session常用方法

HttpServletRequest 接口中提供了两个方法来创建 HttpSession 实例,并进行一些简单的操作,通过看Session的源码可以看出:

public interface HttpSession {
    long getCreationTime();
    String getId();
    long getLastAccessedTime();
    ServletContext getServletContext();
    void setMaxInactiveInterval(int var1); //设置 Session 的有效时间
    int getMaxInactiveInterval();
    /** @deprecated */
    @Deprecated
    HttpSessionContext getSessionContext();
    Object getAttribute(String var1);
    /** @deprecated */
    @Deprecated
    Object getValue(String var1);
    Enumeration<String> getAttributeNames();
    /** @deprecated */
    @Deprecated
    String[] getValueNames();
    void setAttribute(String var1, Object var2);
    /** @deprecated */
    @Deprecated
    void putValue(String var1, Object var2);
    void removeAttribute(String var1);
    /** @deprecated */
    @Deprecated
    void removeValue(String var1);
    void invalidate();
    boolean isNew();

Session的缺点

虽然Session很好的避免了Cookie的通信复杂度,但是对于像京东天猫这样服务器就需要记录上亿个session id,这对服务器来说显然不友好,因为线上为了应对大量请求,我们一定是使用服务器集群去分散处理请求的,那么如果用户UA第一次请求了服务器F1,生成了session id,下次请求被路由到了服务器F2,而F2上没有UA的。

  1. 这个时候服务器说,那我这样吧,我把用户请求的服务器记录下来,粘住特定的session id请求到特定的服务器上,这就是session 粘连策略

这样也有风险,万一特定的这个服务器挂了,这个服务器上记录的session就都丢失了,某些特定用户就永远登录不上了

  1. 这个时候服务器说,那这样吧,只要有session产生就复制到各个服务器上,这样就有保障了,这就是session的复制策略

但是这样复制来复制去太麻烦,太耗费性能了,如果有一个统一的机器负责管理session就好了,而且这个统一的机器最好是个集群,这样防止单点挂掉的风险。

  1. 这样就引入了中间件集群这种方式去解决问题,例如引入Redis集群去处理,这就是session 共享策略

但是这样仍然将session置于一个不确定不稳定的状态,因为第三方的集群也可能有宕机的危险

Token机制

那么如果服务端不对session进行记录,只是验证会话有效性呢?也就是客户端第一次请求后服务端依据用户数据计算出一个token签名然后返回给客户端,客户端第二次请求的时候把数据和token签名一起发来,服务端再用相同算法对数据进行签名,得出的token签名一致即认证通过。这就是token的概念

Token解决购物车问题

继续延续上文的购物车问题,有了token之后可以不必考虑session的存储问题,分布式集群问题,按照如下图的流程,我们在

从上图中可以看出,服务端不存储任何内容,token 只存储在浏览器中,服务端却没有存储,server 会有一套校验机制,校验这个 token 是否合法。那么token既然没有存储到服务端,不像 session 那样根据 sessionId 找到 userid,怎么定位请求的用户呢? 其实token 本身携带 uid 信息。

当然其实我们可以看的出,Token并没有在服务端存储数据,那么怎么记录购物车信息呢,其实Token是一种轻量级的用户认证方案,只能认证当前用户登录的有效性,而并不能追踪用户行为,所以可以说Token在用户验证领域很专业,但是用途比较单一,如果要解决这里的购物车问题,还需要token结合一些数据存储技术,例如数据库或者redis,token负责验证当前用户,数据库或者redis负责进行购物车存取操作。

Token的基本概念

token 验证是无状态的,服务器不记录哪些用户登录了或者哪些 JWT 被发布了,而是每个请求都带上了服务器需要验证的 token,token 放在了 Authorization header 中,形式是 Bearer { JWT },但是也可以在 post body 里发送,甚至作为 query parameter。JWT的结构如下;

可以看到 token 主要由三部分组成:

  • header:指定了签名算法。
  • payload:可以指定用户 id,过期时间等非敏感数据。
  • Signature:签名,server 根据 header 知道它该用哪种签名算法,再用密钥根据此签名算法对 head+payload 生成签名,这样一个 token 就生成了。

当 server 收到浏览器传过来的 token 时,它会首先取出 token 中的 header+payload,根据密钥生成签名,然后再与 token 中的签名比对,如果成功则说明签名是合法的,即 token 是合法的。而且你会发现 payload 中存有我们的 userId,所以拿到 token 后直接在 payload 中就可获取 userid,避免了像 session 那样要从 redis 去取的开销。

整体的验证流程如下:

  1. 用户输入登录信息
  2. 服务器判断登录信息正确,返回一个 token
  3. token 存储在客户端,大多数通常在 local storage,但是也可以存储在 session storage 或者 cookie 中。
  4. 接着发起请求的时候将 token 放进 Authorization header,或者同样可以通过上面的方式。
  5. 服务器端解码 JWT 然后验证 token,如果 token 有效,则处理该请求。
  6. 一旦用户登出,token 在客户端被销毁,不需要经过服务器端。

可以看的出,token就是一个令牌的概念。

Token的缺点

既然 token 这么好,那为什么我们依然大多数使用场景还是Session?token也有它自身的缺点:

  1. token 太长了,token 是 header,payload 编码后的样式,所以一般要比 sessionId 长很多,很有可能超出 cookie 的大小限制(cookie 一般有大小限制的,如 4kb)。如果你在 token 中存储的信息越长,那么 token 本身也会越长,这样的话由于你每次请求都会带上 token,对请求来是个不小的负担。
  2. token不太安全,我们说 token 是存在浏览器的,既然它太长放在 cookie 里可能导致 cookie 超限,那就只好放在 local storage 里。这样会造成安全隐患,因为 local storage 这类的本地存储是可以被 JS 直接读取的。

所以 token 更适合一次性的命令认证,设置一个比较短的有效期

应用场景的划分

详细的比较下Session、Cookie以及Token的区别和应用场景。

Session和Cookie的使用场景区别

Cookie 和 Session都是⽤来跟踪浏览器⽤户身份的会话⽅式,但是两者的应⽤场景不太⼀样。

  • Cookie⼀般⽤来保存⽤户信息,通过这种方式不需要用户反复去输入用户信息去登录
  • 我们在 Cookie 中保存已经登录过得⽤户信息,下次访问⽹站的时候⻚⾯可以⾃动帮你登录的⼀些基本信息给填了;
  • ⼀般的⽹站都会有保持登录也就是说下次你再访问⽹站的时候就不需要重新登录了,这是因为⽤户登录的时候我们可以存放了⼀个 Token 在 Cookie中,下次登录的时候只需要根据 Token 值来查找⽤户即可(为了安全考虑,重新登录⼀般要将 Token重写);
  • 登录⼀次⽹站后访问⽹站其他⻚⾯不需要重新登录
  • Session 的主要作⽤就是通过服务端记录⽤户的状态。 典型的场景是购物⻋,当你要添加商品到购物⻋的时候,系统不知道是哪个⽤户操作的,因为 HTTP 协议是⽆状态的。服务端给特定的⽤户创建特定的 Session 之后就可以标识这个⽤户并且跟踪这个⽤户了。
  • Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。Cookie 存储在客户端中,⽽Session存储在服务器上,相对来说 Session 安全性更⾼。如果要在Cookie 中存储⼀些敏感信息,不要直接写⼊ Cookie 中,最好能将 Cookie 信息加密然后使⽤到的时候再去服务器端解密

可以通俗的这么理解,Cookie在客户端保存用户信息,知道用户干了什么事儿,Session则在服务端给用户创建了相应的数据存档,下次只要对应用户登录就能读档,那么要想找到用户正确的访问内容需要二位配合了。

JWT 和 Session+Cookies 基于身份验证的区别

JWT 和 Session Cookies 都提供安全的用户身份验证,但是它们有以下几点不同

  • 密码签名,JWT 具有加密签名,而 Session Cookies 则没有。
  • 连接状态:JWT 是无状态的,因为声明被存储在客户端,而不是服务端内存中。身份验证可以在本地进行,而不是在请求必须通过服务器数据库或类似位置中进行。 这意味着可以对用户进行多次身份验证,而无需与站点或应用程序的数据库进行通信,也无需在此过程中消耗大量资源。
  • 可扩展性,Session Cookies 是存储在服务器内存中,这就意味着如果网站或者应用很大的情况下会耗费大量的资源。由于 JWT 是无状态的,在许多情况下,它们可以节省服务器资源。因此 JWT 要比 Session Cookies 具有更强的可扩展性。
  • 是否可跨域,JWT 支持跨域认证,Session Cookies 只能用在单个节点的域或者它的子域中有效。如果它们尝试通过第三个节点访问,就会被禁止。如果你希望自己的网站和其他站点建立安全连接时,这是一个问题。使用 JWT 可以解决这个问题,使用 JWT 能够通过多个节点进行用户认证,也就是我们常说的跨域认证。

需要说明的是,JWT仅仅是一种用户身份认证的解决方案,如果我们进行购物车的设置,JWT搞不定。

总结一下

这篇Blog参照了很多内容,共写了两天,有很多地方也很纠结没有想通,现在进行一下思路整理吧:

  • cookie就是由服务端生成返回给客户端的,并且在第一次生成时默认给放置了一个Jsession id
  • 浏览器请求服务端时,通过Jsessionid找到对应的session,然后进行对应存取操作,这个查找过程是tomcat帮我们做的,我们在代码里不需要关心,也就是说我们不用调用什么通过sessionid获取session对象之类的方法。
  • 一个客户端浏览器对应一个目标站点【localhost】只产生一个session、一个cookie,我们追踪用户会话,前提也是在这个范围内讨论的,如何跨域生效、如何跨浏览器记忆之类的问题不在讨论范围内,我们只专注于当前客户端浏览器对于当前目标站点是如何进行会话追踪的
  • JSP中的内置对象session就是和Servle中的这个HttpSession生成的,所以之前我们讨论JSP的作用域时其实也大概提了下,只是当时还不知道为什么session的作用域是一个浏览器,并且为什么能跨请求,现在我们知道了,是因为整个浏览器请求目标站点范围内,用的sessionid是同一个,如果cookie一直存在,那么HttpSession就还是那一个,所以作用范围才是这么大。而request只不过是doGet的一个局部变量,当然会请求一次后作用域失效。
  • 对于用户身份验证这种场景,最好用token,因为session操作起来太复杂,没必要为了验证用户身份搭建一套redis,而如果有其它诉求,例如追踪用户的一些信息,还是要使用cookie+session,因为token不在服务端存储数据。
  • cookie+session这种组合用途也要细分,cookie由于存储在客户端本地,所以最好不要放一些敏感信息,而cookie由于存放在客户端本地,所以可以存放很长时间,所以cookie适合防止要求长期存储但不敏感的数据,而session由于运行在服务器内存中或者redis集群中,而且通常有个过期时间,所以适合存放一些短期但重要的数据
  • 当禁用cookie时,需要URL重写技术对Session进行跟踪,这里不做更多讨论
  • 隐藏表单域提交也属于一种会话追踪技术,这里也不做更多的讨论

本篇Blog提到的购物车的例子,由于购物车的信息在各大电商网站比较重要,所以一般会落库并进行数据分析,所以不一定是存储在session或者cookie中,这里只是举个例子,具体以业务场景为准。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
相关文章
|
4月前
|
缓存 前端开发 应用服务中间件
Web端实时通信技术SSE在携程机票业务中的实践应用
本文介绍了携程机票前端基于Server-Sent Events(SSE)实现服务端推送的企业级全链路通用技术解决方案。文章深入探讨了 SSE 技术在应用过程中包括方案对比、技术选型、链路层优化以及实际效果等多维度的技术细节,为类似使用场景提供普适性参考和借鉴。
107 7
|
10月前
|
人工智能 安全 物联网
区块链技术的未来展望:去中心化金融(DeFi)与Web 3.0的融合
区块链技术的未来展望:去中心化金融(DeFi)与Web 3.0的融合
|
10月前
|
前端开发 JavaScript 搜索推荐
HTML与CSS在Web组件化中的核心作用及前端技术趋势
本文探讨了HTML与CSS在Web组件化中的核心作用及前端技术趋势。从结构定义、语义化到样式封装与布局控制,两者不仅提升了代码复用率和可维护性,还通过响应式设计、动态样式等技术增强了用户体验。面对兼容性、代码复杂度等挑战,文章提出了相应的解决策略,强调了持续创新的重要性,旨在构建高效、灵活的Web应用。
188 6
|
10月前
|
XML 前端开发 JavaScript
PHP与Ajax在Web开发中的交互技术。PHP作为服务器端脚本语言,处理数据和业务逻辑
本文深入探讨了PHP与Ajax在Web开发中的交互技术。PHP作为服务器端脚本语言,处理数据和业务逻辑;Ajax则通过异步请求实现页面无刷新更新。文中详细介绍了两者的工作原理、数据传输格式选择、具体实现方法及实际应用案例,如实时数据更新、表单验证与提交、动态加载内容等。同时,针对跨域问题、数据安全与性能优化提出了建议。总结指出,PHP与Ajax的结合能显著提升Web应用的效率和用户体验。
213 3
|
10月前
|
缓存 安全 前端开发
构建高效Web应用的五大关键技术
【10月更文挑战第42天】在数字化浪潮中,Web应用已成为企业与用户互动的重要桥梁。本文将深入探讨提升Web应用性能和用户体验的五项核心技术,包括前端优化、后端架构设计、数据库管理、安全性增强以及API开发的最佳实践。通过这些技术的应用,开发者可以构建出更快、更稳定且更安全的Web应用,满足现代网络环境的需求。
|
4月前
|
Web App开发 前端开发 JavaScript
鸿蒙5开发宝藏案例分享---Web适配一多开发实践
这是一份实用的鸿蒙Web多设备适配开发指南,针对开发者在不同屏幕尺寸下的布局难题提供了解决方案。文章通过三大法宝(相对单位、媒体查询和窗口监听)详细介绍如何实现智能适配,并提供了多个实战案例,如宫格布局、对话框变形和自适应轮播图等。此外,还分享了调试技巧及工具推荐,帮助开发者快速上手并优化性能。最后鼓励读者实践探索,并提示更多官方资源等待发现。
|
11月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
581 3
|
6月前
|
关系型数据库 MySQL 数据库
基于Flink CDC 开发,支持Web-UI的实时KingBase 连接器,三大模式无缝切换,效率翻倍!
TIS 是一款基于Web-UI的开源大数据集成工具,通过与人大金仓Kingbase的深度整合,提供高效、灵活的实时数据集成方案。它支持增量数据监听和实时写入,兼容MySQL、PostgreSQL和Oracle模式,无需编写复杂脚本,操作简单直观,特别适合非专业开发人员使用。TIS率先实现了Kingbase CDC连接器的整合,成为业界首个开箱即用的Kingbase CDC数据同步解决方案,助力企业数字化转型。
977 5
基于Flink CDC 开发,支持Web-UI的实时KingBase 连接器,三大模式无缝切换,效率翻倍!
|
6月前
|
机器学习/深度学习 开发框架 API
Python 高级编程与实战:深入理解 Web 开发与 API 设计
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧以及数据科学和机器学习。本文将深入探讨 Python 在 Web 开发和 API 设计中的应用,并通过实战项目帮助你掌握这些技术。
|
10月前
|
开发框架 搜索推荐 数据可视化
Django框架适合开发哪种类型的Web应用程序?
Django 框架凭借其强大的功能、稳定性和可扩展性,几乎可以适应各种类型的 Web 应用程序开发需求。无论是简单的网站还是复杂的企业级系统,Django 都能提供可靠的支持,帮助开发者快速构建高质量的应用。同时,其活跃的社区和丰富的资源也为开发者在项目实施过程中提供了有力的保障。
352 67

热门文章

最新文章