Spring Session - Cookie VS Session VS Token 以及 Session不一致问题的N种解决方案

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Spring Session - Cookie VS Session VS Token 以及 Session不一致问题的N种解决方案

20210215205331403.png

Cookie VS Session VS Token

我们在学习Spring Session 之前, 先聊聊 几种主流的会话方式以及发展历史


History


众所周知 HTTP请求是无状态的, 随着交互式Web应用的兴起,要管理会话,那必须记住哪些人登录了系统, 怎么办呢?


大家就想着颁发一个会话标识(session id), 实际上呢就是一个随机字符串,每个人收到的都不一样 。


这样每次向系统发起HTTP请求的时候,把这个字符串给一并捎过来, 这样服务端就可以很好地区分了。


举个例子试想一下, 两个节点组成了一个集群, 用户一通过节点A登录了系统, 那session id会保存在节点A上,如果用户一的下一次请求被转发到节点B怎么办?节点B可没有用户一的 session id 即相当于用户一没有登录呀~


当然了可以通过一些办法解决,比如ng上开启 session sticky , 就是让用户一的请求一直粘连在节点A上 。 但是节点A挂掉了, 还得转到节点B去。 那就是session 的复制呗, 把session id 在两个节点之间同步(tomcat之间进行session复制) 。


20210215214708814.png


随着用户数据量的激增 , 每个人只需要保存自己的session id,而服务器要保存所有人的session id , 对服务器说是一个巨大的开销 , 严重的限制了服务器扩展能力 。


后来呢,大家说把session放到外面来管理吧 ,这样就不用复制来复制去了 ,数据量大了还影响带宽。。。。


20210215214937825.png



艾玛 ,你这个存储session的,还是个单点呀,那我还得确保你这个节点高可用啊。。。。服务端想想说 ,我不管这些破session行不行 ,让你客户端去管你自己的这些数据呀?


又进化出了一版本 TOKEN


可是如果不保存这些session id , 怎么验证客户端发给服务端的session id 的确是服务端生成的呢? 如果不去验证, 都不知道他们是不是合法登录的用户, 那…为所欲为了。


so , 重点来了, 服务端验证合法性


举个例子,用户一已经登录了系统,服务端给用户一发一个令牌(token), 里边包含了用户一的 user id等信息, 下一次用户已再次通过Http 请求访问服务端的时候, 把这个token 通过Http header 带过来 就可以了。


等等, 那别人伪造怎么办? 怎么让别人伪造不了呢?


数据签名


比如哈, 管理端用HMAC-SHA256 算法,加上一个只有管理端自己才知道的密钥, 对数据做一个签名, 把这个签名和数据一起作为token , 由于密钥别人不知道, 就无法伪造token了。

20210215220838178.png


token 管理端 不保存, 当用户把这个token 发过来的时候,管理端再用同样的HMAC-SHA256 算法和同样的密钥,对数据再计算一次签名, 和token 中的签名做个比较, 如果相同, 这认为已经登录过了,并且可以直接取到存储在其中的的user id , 如果不相同, 数据部分肯定被人篡改过, 即为没有认证。


20210215221404442.png



Token 中的数据是明文保存的(虽然会用Base64做下编码, 但不是加密), 还是可以被别人看到的, 所以Token中不能在其中保存像密码等敏感信息。


当然, 如果一个人的token 被别人偷走了,那其他用户使用该token登录 也会被认为合法用户, 这其实和一个人的session id 被别人偷走道理是一样的 。 只能防止篡改,不能防止泄露


这样一来, 管理端就不保存session id 了, 只负责生成token , 然后验证token ,消除了session id 这个负担, 那么管理端的集群现在可以轻松地做水平扩展, 用户访问量激增, 加节点…




20210215221746962.png



Cookie


cookie 指的是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。


cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。


由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。


Session


session 简单来说就是服务器给每个客户端分配的“身份标识”,然后客户端每次向服务器发请求的时候,都带上这个“身份标识”,服务器就知道这个请求来自于谁了。


至于客户端怎么保存这个“身份标识”,可以有很多种方式,对于浏览器客户端,大家都默认采用 cookie 的方式。


服务器使用session把用户的信息临时保存在了服务器上,用户退出后session会被销毁。这种用户信息存储方式相对cookie来说更安全.


Token


为什么非要用token呢?

我们都是知道HTTP协议是无状态的,这种无状态意味着程序需要验证每一次请求,从而辨别客户端的身份。


在这之前,程序都是通过在服务端存储的登录信息来辨别请求的。这种方式一般都是通过存储Session来完成。


随着Web,应用程序以及移动端的兴起,这种验证的方式逐渐暴露出了问题。尤其是在可扩展性方面。


主要存在一下几个问题


Seesion: 每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来越多的用户发请求时,内存的开销也会不断增加。

可扩展性: 在服务端的内存中使用Seesion存储登录信息,伴随而来的是可扩展性问题。

CORS(跨域资源共享): 当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会是一个让人头疼的问题。在使用Ajax抓取另一个域的资源,就可以会出现禁止请求的情况。

CSRF(跨站请求伪造): 用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用其访问其他的网站。


在这些问题中,可扩展行是最突出的。因此有必要去寻求一种更有行之有效的方法, TOKEN就随之而来


主要流程如下:

20210215225055622.png


  1. 用户通过用户名和密码发送请求。
  2. 程序验证。
  3. 程序返回一个签名的token 给客户端。
  4. 客户端储存token,并且每次用于每次发送请求。
  5. 服务端验证token并返回数据。


优点

  • 无状态、可扩展
  • 支持移动设备
  • 跨程序调用
  • 安全

好了,扯皮结束了,我们先关注分布式环境下Session的解决方案, 至于Token我会结合JWT来分享 。



20210215225907535.png

Session不一致问题


假设我们的应用部署在Tomcat中

【单个节点的tomcat 】


浏览器在第一次访问服务器Tomcat1时,发现请求的 Cookie 中不存在 sessionid ,所以创建一个 sessionid 为 xxxxxxx 的 Session ,同时将该 sessionid 写回给浏览器的 Cookie 中。


浏览器在下一次访问 Web 服务器 时,Tomcat1会发现请求的 Cookie 中已存在 sessionid 为 xxxxxxx ,则直接获得 xxxxxxx 对应的 Session 。


【多个节点的tomcat 】


在多台 Tomcat 的情况下,采用 Nginx 做负载均衡。


接上面的请求,继续 浏览器又发起一次请求访问 Web 服务器,Nginx 负载均衡转发请求到 Tomcat2 上。Tomcat2 会发现请求的 Cookie 中已存在 sessionid 为 X ,则直接获得 xxxxxxx 对应的 Session 。结果 Tomcat2 在JVM中找不到 xxxxxxx 对应的 Session


这样就会出现 Session 不一致的问题 。


Session不一致解决方案

nginx session sticky

使用 Nginx 实现会话黏连,将相同 sessionid 的浏览器所发起的请求,转发到同一台服务器。这样,就不会存在多个 Web 服务器创建多个 Session 的情况,也就不会发生 Session 不一致的问题。

不过,这种方式目前基本不被采用。 如果一台服务器重启,那么会导致转发到这个服务器上的 Session 全部丢失。

主要是安装 nginx-sticky-module

下载地址: https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/master.tar.gz

# tar zxf nginx-goodies-nginx-sticky-module-ng-1e96371de59f.tar.gz
# mv nginx-goodies-nginx-sticky-module-ng-1e96371de59f nginx-sticky
# tar zxf nginx-1.6.1.tar.gz
# cd nginx-1.6.1
# ./configure --prefix=/app/nginx --with-http_gzip_static_module --with-http_flv_module --with-http_dav_module --with-http_stub_status_module --with-http_realip_module --add-module=/app/soft/nginx-sticky/
# make
# cd /app/nginx/sbin
# mv nginx nginx.old
# cp /app/soft/nginx-1.6.1/objs/nginx ./
# cd -
# make upgrade

配置

upstream artisan{
    sticky;
    server 172.168.15.11:8001;
    server 172.168.15.12:8002;
    ....
}
server {
        listen       80;
        server_name  localhost;
        .....
        location ~/xxxxx/.*\.jsp|do|htm$ {
                proxy_pass      http://artisan;
                .....
        } 
}   


不用第三方的模块包的话,那就使用ip_hash的策略。这种方案的局限性是ip不能变。


Tomcat session 复制

Web 服务器之间,进行 Session 复制同步。仅仅适用于实现 Session 复制的 Web 容器,例如 Tomcat

不过,这种方式目前基本也不被采用。 session数据量大的时候,复制效率低,占用带宽等等弊端。


Session 外部化存储


Session 外部化存储 即将 Session 存储外部化,持久化到 MySQL、Redis、MongoDB 等中。这样一搞Tomcat 就可以无状态化,专注作为Web 服务 ,扩容也变得容易。


主要由两种方式


方式一:基于 Tomcat、Jetty 等 Web 容器自带的拓展,使用读取外部存储器的 Session 管理器 ,使用的较少,这里不做讨论

基于Tomcat的tomcat-redis-session-manager插件,基于Jetty的jetty-session-redis插件、memcached-session-manager插件;


好处是对项目来说是透明的,无需改动代码,但是由于过于依赖容器,一旦容器升级或者更换意味着又得重新配置;其实底层是,复制session到其它服务器,所以会有一定的延迟,也不能部署太多的服务器。


网上找了两篇文章,感兴趣的可以参考下


Tomcat会话管理器(Tomcat Session Manager)

Jetty集群配置Session存储到MySQL、MongoDB


方式二:基于应用层封装 HttpServletRequest 请求对象,包装成自己的 RequestWrapper 对象,从而让实现调用 HttpServletRequest#getSession() 方法时,获得读写外部存储器的 SessionWrapper 对象 。 比如 Spring Session解决方案


使用Spring session框架提供的会话管理工具, 这个方案既不依赖tomcat容器,又不需要改动代码, 是目前非常完美的session共享解决方案。


我们这里只讨论 Spring Session提供的解决方案 ,支持外部存储包括 Redis . 数据库、Hazelcast、MongoDB等


20210215234037757.png

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
67 2
|
28天前
|
存储 安全 搜索推荐
理解Session和Cookie:Java Web开发中的用户状态管理
理解Session和Cookie:Java Web开发中的用户状态管理
50 4
|
1月前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
2月前
|
缓存 Java Spring
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
文章比较了在Servlet和Spring Boot中获取Cookie、Session和Header的方法,并提供了相应的代码实例,展示了两种方式在实际应用中的异同。
179 3
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
|
2月前
|
前端开发 Java C++
RSocket vs WebSocket:Spring Boot 3.3 中的两大实时通信利器
本文介绍了在 Spring Boot 3.3 中使用 RSocket 和 WebSocket 实现实时通信的方法。RSocket 是一种高效的网络通信协议,支持多种通信模式,适用于微服务和流式数据传输。WebSocket 则是一种标准协议,支持全双工通信,适合实时数据更新场景。文章通过一个完整的示例,展示了如何配置项目、实现前后端交互和消息传递,并提供了详细的代码示例。通过这些技术,可以大幅提升系统的响应速度和处理效率。
|
2月前
|
存储 安全 数据安全/隐私保护
Cookie 和 Session 的区别及使用 Session 进行身份验证的方法
【10月更文挑战第12天】总之,Cookie 和 Session 各有特点,在不同的场景中发挥着不同的作用。使用 Session 进行身份验证是常见的做法,通过合理的设计和管理,可以确保用户身份的安全和可靠验证。
25 1
|
3月前
|
存储 缓存 数据处理
php学习笔记-php会话控制,cookie,session的使用,cookie自动登录和session 图书上传信息添加和修改例子-day07
本文介绍了PHP会话控制及Web常用的预定义变量,包括`$_REQUEST`、`$_SERVER`、`$_COOKIE`和`$_SESSION`的用法和示例。涵盖了cookie的创建、使用、删除以及session的工作原理和使用,并通过图书上传的例子演示了session在实际应用中的使用。
php学习笔记-php会话控制,cookie,session的使用,cookie自动登录和session 图书上传信息添加和修改例子-day07
|
2月前
|
Java 关系型数据库 开发工具
idea创建不了spring2.X版本,无法使用JDK8,最低支持JDK17 , 如何用idea创建spring2.X版本,使用JDK8解决方案
本文提供了解决方案,如何在IDEA中创建Spring 2.X版本的项目并使用JDK8,尽管Spring 2.X已停止维护且IDEA不再直接支持,通过修改pom.xml或使用阿里云的国内源来创建项目。
96 0
idea创建不了spring2.X版本,无法使用JDK8,最低支持JDK17 , 如何用idea创建spring2.X版本,使用JDK8解决方案
|
3月前
|
Java 开发工具 对象存储
简化配置管理:Spring Cloud Config与Netflix OSS中的动态配置解决方案
简化配置管理:Spring Cloud Config与Netflix OSS中的动态配置解决方案
51 2
|
2月前
|
Python
python中3种获取cookie解决方案
python中3种获取cookie解决方案
23 0