某一天,有同事在产品问题反馈大群里扔出一个问题:登录后隔两天再打开页面就需要重新登录。在查问题的过程中,我们有提到 7 天过期,老板对此也扔出了一个问题:为什么是 7 天。于是有了今天这篇文章。首先我们先看一下 WEB 会话管理的概念。
在说 WEB 会话管理之前,我们需要先说一下 HTTP 协议。这里我们暂且不提 HTTP2.0 和 HTTP3.0,本文的 HTTP 协议主要是指现在使用最广泛,使用历史最久的 HTTP1.x 版本。
HTTP 协议是一个无连接无状态的协议。
在互联网早期,这样的协议完全能满足 WEB 应用的需要,但随着 WEB 应用复杂度的提升,识别用户是谁,记录用户的行为,有用户状态变成了刚性需求,或者说是核心功能点。
此时,会话管理应运而生,会话管理是一种控制和维持用户交互状态的机制,它定义了一系列用于管理用户和 WEB 应用系统交互状态的措施。
会话管理有两个关键点,一个是怎样存储,另一个是存储什么。
在早期的有会话管理的 WEB 应用中,会话经常存储在服务端的 session 中,常见的 WEB 编程语言都自带 session 的处理逻辑,如 PHP 和 Java 都默认带有 session 的扩展或组件,如 PHP 中的全局变量 $_SESSION。客户端存储 sessionid,通过 cookie / 隐藏的表单字段 / 重写 URL 的方式传递 sessionid,后两种方式一般在禁用 cookie 的时候启用。
服务端 seesion 的优势
针对服务端的存储方式,就会出现了我们在前言中提到的过期问题,为什么要有过期?
有了过期,就会有过期时间,常见的过期时间有 1 小时( 3600 秒),2 小时( 7200 秒),1 天, 7 天(一周),15 天,30 天(一个月)。这个过期时间更多的是经验值,或者说是一个大家认为合适的值。这个值对业务的影响,貌似没有一个客观的评价。
服务端的 seesion 的常见存储方案如下:
考虑到服务器的负担和架构的复杂性,依赖于浏览器每次请求都会带上 cookie 的特性,我们可以把用户的登录凭证直接存到客户端(浏览器)中。当用户登录成功之后,把登录凭证写到 cookie 里面,并给 cookie 设置有效期,后续请求直接验证存有登录凭证的 cookie 是否存在以及凭证是否有效,即可判断用户的登录状态。当年 Rails 的默认会话存储方案是用 cookie 方案。
优点
缺点
相对于前面两种方案,令牌方案是前后端分离后的常见方案,session 的概念在这个方案里面更淡一些。
在讲令牌方案之前我们先讲一下安全上两个非常重要的点:认证和授权。有时候会把这两个东西弄混,其定义如下:
区别 | 认证 | 授权 |
---|---|---|
作用 | 确定用户所宣称的身份 | 确定用户可访问的权限 |
方式 | 通过合法凭证校验用户 | 通过规则和策略校验访问 |
时机 | 早于授权 | 在认证成功后执行 |
实现 | 通过 ID tokens 实现 | 用 Access Tokens 实现 |
基于令牌的认证和授权是现在最流行的的一种技术,当用户在某处输入一次其用户名和密码后,作为交换会得到一个唯一生成的已加密令牌。该令牌随后会替代登陆凭证,用以访问受保护的页面或资源。当我们要进行下一步的业务操作,会通过令牌获取到用户的的信息和会话的信息,此时一般是通过进程内的缓存或者某个特定的微服务来获取,这些微服务的后端的存储因公司技术架构和服务而异。一般来说,对于海量的互联网应用来说,这里最终还是从内存中获取的数据,如前面说到的分布式 NoSQL 数据库。
一个令牌就是服务器生成的一段数据,包含了唯一性识别一个用户的信息,一般被生成为一长串随机字符和数字。
比如看起来可能像这样:bb74324734bcf34748bb08bu2842f3288
或更复杂些比如: eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ1bXMiLCJzdWIiOjY1NDA1NjI5NCwiYXVkIjoiZ2FvZGluZ3giLCJleHAiOjE2NDE5MDgzOTZ9.8MZaB55vlScSDZxBmO-N9ol9UTvYPVvgUFab7dFe6fY
这个令牌本身是无意义和无用的,但结合适当的令牌体系,就会变成保证应用安全性的重要一环。
基于令牌的实现过程一般如下:
我们常用的基于令牌的方案有两个:
关于这两个方案的更详细的说明见:深入 OAuth2.0 和 JWT
在令牌的生命周期中存在两个较大的安全薄弱环节:
会话从本质上来讲是一种缓存,一种强关联用户的缓存,所以与用户相关的一些常用数据可以放在会话里面,如头像、用户名、真实姓名、积分等等。这些缓存数据都只读,当要更新时还是需要从数据库中获取后再次更新写入,而不是依赖于会话的数据。
除了常用数据我们经常还会把购物车或者菜单、权限等写入会话中,此时除了用户属性,还带上了业务属性,我们可以称之为业务会话。业务会话一般是指某个业务场景中需要临时缓存起来的数据,而且这部分数据大多数是要落库的,当用户会话过期重新登录后这部分数据还是要从数据库重新获取,加载到会话中。
最后这个问题的原因是业务逻辑里面针对不同的渠道有抢登的业务逻辑,而这个逻辑在某个时候是不需要触发的。
此外,老板最后在群里问了一个触及灵魂的问题:
我们做一件事情的时候是依着惯性做事,还是基于深度思考后的决策?
自我反省中~~
这篇文章墨迹了几周,一直不知道如何写好,总觉得写得有点不如人意,如果往协议本身来写,好像也没有想写的,就这样吧。
你好,我是潘锦,超过 10 年的研发管理和技术架构经历,出过书,创过业,带过百人团队,也在腾讯,A 股上市公司呆过一些年头,现在在一家 C 轮的公司负责一些技术方面的管理工作。早年做过 NOI 和 ACM,对前端架构、跨端、后端架构、云原生、DevOps 等技术始终保持着浓厚的兴趣,平时喜欢读书、思考,终身学习实践者,欢迎一起交流学习。微信公众号:架构和远方,博客: www.phppan.com