用户登录一个 SaaS 平台之后,浏览器为什么不需要每次访问页面都重新输入密码?原因并不神秘:服务器在用户完成身份认证后,会建立一种能够持续识别用户的凭证,浏览器或者客户端在后续请求中携带这份凭证,服务器再根据它判断“这是刚才那个已经登录的用户”。
这个过程看起来简单,背后却涉及 Cookie、Session、Token、JWT、数据库、Redis、HTTPS、CSRF、权限控制以及多租户隔离等多个机制。
很多技术文章把 Cookie、Session 和 Token 当成三种互相排斥的登录方案,这是一个常见误区。Cookie 是客户端保存和发送数据的 HTTP 机制,Session 是一种服务器端会话状态管理方式,Token 是一种身份凭证。三者完全可以同时出现。
一个典型的 SaaS 网站甚至可能使用 Cookie 保存 Session ID,而服务器使用 Redis 保存 Session 数据;另一个系统则可能把短期 Access Token 放进 HttpOnly Cookie;微服务内部再使用 Bearer Token 传递身份信息。
理解它们之间的关系,比简单讨论“Session 还是 Token”更加重要。
Cookie 到底保存了什么
Cookie 本质上是浏览器按照 HTTP Cookie 机制保存的一小段数据。
服务器可以通过 Set-Cookie 响应头要求浏览器保存 Cookie,之后浏览器访问符合 Domain、Path、Secure、SameSite 等条件的网站时,可以自动把 Cookie 放到请求中。
Cookie 本身并不等于登录状态。
例如服务器可以设置一个 Cookie:
session_id=8f3c...
这个 Cookie 只保存一个随机的 Session ID,真正的用户信息仍然保存在服务器端。
也可以把一个签名 Token 放进 Cookie。
因此,“Cookie 方案”和“Session 方案”实际上并不是同一个层面的概念。
Cookie 最大的优势之一,是浏览器能够自动管理它。Web 应用不需要每次由 JavaScript 手动读取 Cookie 并添加到 HTTP 请求中。
对于登录系统,这种特性非常方便。
但 Cookie 也带来了一个重要的安全问题:浏览器会自动发送符合条件的 Cookie,因此网站需要认真配置 Cookie 的安全属性。
Secure 可以要求 Cookie 只通过 HTTPS 发送。
HttpOnly 可以阻止普通 JavaScript 通过 document.cookie 读取该 Cookie,从而降低一部分 XSS 导致的会话凭证直接泄露风险。
SameSite 则可以限制跨站请求携带 Cookie 的方式,对降低 CSRF 风险非常重要。
因此企业 SaaS 网站通常应该认真设计 Cookie 属性,而不是简单地说“Cookie 不安全”。
Cookie 存储的数据通常也受到浏览器实现和协议限制,单个 Cookie 通常只能保存几 KB,因此它并不适合承载庞大的用户权限数据或者复杂业务对象。
更重要的是,不应该把密码、长期密钥或者其他高敏感信息直接放进 Cookie。
Session 是什么
Session 可以理解成服务器端维护的一份登录状态。
用户输入正确的账号和密码后,服务器生成一个具有足够随机性的 Session ID,然后把这个 ID 发给浏览器。
浏览器后续请求带上 Session ID,服务器根据这个 ID 找到对应的会话数据。
例如服务器端可能保存:
Session ID
用户 ID
登录时间
过期时间
认证状态
部分安全上下文
浏览器保存的可能只有:
session_id=随机字符串
这时候真正的登录状态仍然在服务器端。
这就是传统 Session 模型。
它有一个很重要的优点:服务器可以随时删除或者使某个 Session 失效。
例如管理员强制某个账户退出所有设备,服务器删除相关 Session,后面的请求就无法继续通过认证。
密码修改、账户被盗、管理员强制下线等场景,也可以通过撤销 Session 来快速终止登录状态。
为什么 Redis 经常出现在 SaaS Session 架构里
假设 SaaS 网站只有一台服务器,Session 可以直接保存在这台服务器的内存或者本地存储中。
但企业 SaaS 通常会有多台 Web Server。
用户第一次请求进入服务器 A,Session 保存在 A;下一次请求进入服务器 B,如果 B 根本不知道这个 Session,就会出现用户刚登录又被要求登录的情况。
一种传统解决办法是 Sticky Session,让同一个用户尽量一直访问同一台服务器。
但这会增加负载均衡和故障处理的复杂性。
更常见的方法,是把 Session 放到共享存储,例如 Redis。
这样服务器 A 和服务器 B 都可以根据 Session ID 查询同一份会话数据。
这并不意味着“Session 不再需要服务器存储”。
恰恰相反,Redis 仍然是服务器端 Session 存储,只不过它把会话数据从单台 Web Server 转移到了共享的分布式存储层。
因此,Redis Session 需要考虑过期时间、容量、故障转移、连接管理、网络延迟和数据恢复等问题。
对于大型 SaaS,Session 数量可能随着活跃登录会话增加而增加,但实际内存占用取决于每个 Session 保存多少数据,而不是简单按照用户数量计算。
Token 又是什么
Token 可以理解为客户端持有的一份身份凭证。
服务器验证 Token 后,就可以判断请求来自哪个身份或者具有什么授权上下文。
Token 可以非常简单,也可以包含结构化数据。
JWT 是其中一种标准化的 Token 格式。
JWT 通常由 Header、Payload 和 Signature 组成。
需要特别强调:JWT 的签名不是“把 Token 加密”。
JWT Payload 在典型 JWS 使用方式下并不是为了保密设计的。它通常可以被客户端解码读取,Signature 的作用主要是让服务器验证 Token 是否被篡改以及是否由可信签发方产生。
如果 Token 中存在敏感信息,就不能因为它叫 JWT 就认为这些数据天然保密。
如果需要机密性,则应该使用适当的加密机制,而不是把“签名”误认为“加密”。
Token 为什么经常被称为无状态认证
所谓无状态,通常是指服务端不需要为每一个 Access Token 保存一份完整的服务器端 Session 数据。
例如 JWT 中可以包含用户 ID、租户 ID、Issuer、Audience、过期时间以及权限相关声明。
服务器验证签名、Issuer、Audience 和有效期后,就可以获得这些信息。
在这种架构下,多台 API Server 可以独立验证 Token,不需要每次都查询同一个 Session Store。
这对于微服务和水平扩展非常方便。
但“无状态”并不意味着系统永远不需要数据库。
服务器仍然可能需要查询用户状态、账户是否被禁用、租户是否有效、具体资源是否属于当前用户等信息。
尤其是授权问题,不能简单地认为“JWT 里写了 admin,所以用户就是 admin”。
权限最终必须由服务器根据当前资源和业务规则进行验证。
Token 并不是签发以后永远不能撤销
原稿把 Token 描述成“一旦签发就无法直接修改或撤销”,这个说法需要更准确一些。
一个 JWT 在其自身有效期内确实可以独立通过签名验证,但企业仍然可以通过多种机制使它提前失效。
例如服务器可以维护 Token Revocation List,也可以维护用户的认证版本号,或者缩短 Access Token 的有效期,再通过 Refresh Token 获取新的 Access Token。
因此真正的工程问题不是“JWT 能不能撤销”,而是企业是否设计了合理的 Token 生命周期和撤销机制。
在线 SaaS 通常不会让一个高权限 Access Token 永久有效。
常见做法是让 Access Token 生命周期相对较短,再使用 Refresh Token 维持较长时间的登录体验。
Refresh Token 本身也需要受到严格保护,并应该考虑轮换、重放检测和撤销。
Cookie、Session 和 Token 可以同时出现
这是理解现代 Web 登录系统最重要的一点。
例如一个 SaaS 网站完全可以采用这样的结构:
浏览器保存一个 HttpOnly、Secure Cookie。
Cookie 里面保存随机 Session ID。
服务器使用 Redis 保存 Session。
API Server 根据 Session ID 获取用户身份。
这是一套 Cookie 加 Session 的架构。
另一套架构可能是:
浏览器保存一个 HttpOnly Cookie。
Cookie 中保存短期 Access Token。
服务器验证 Token 签名和声明。
这是一套 Cookie 加 Token 的架构。
还有一种前端应用可能通过 Authorization Header:
Authorization: Bearer eyJ...
向 API 发送 Access Token。
这时 Token 的传输方式和 Cookie 又是另一回事。
因此讨论 SaaS 登录架构时,应该分别回答三个问题:凭证是什么、凭证存在哪里、服务器如何验证凭证。
这样比简单问“使用 Cookie、Session 还是 Token”更加准确。
为什么 JWT 不一定比 Session 更安全
很多开发人员认为 JWT 是现代方案,所以天然比 Session 安全。
实际上安全性取决于完整架构。
Session ID 如果使用足够高的随机性、通过 HTTPS 传输,并放进配置正确的 HttpOnly Cookie,可以非常安全。
反过来,一个长期有效的 JWT 如果被攻击者窃取,攻击者可能在 Token 过期以前持续使用它。
JWT 的签名只能证明 Token 没有被篡改,并不能阻止 Token 被合法持有者以外的人使用。
这就是 Token Theft,也就是令牌窃取问题。
因此 Token 的保存位置、有效期、轮换、撤销、传输方式以及浏览器安全策略都非常重要。
为什么很多 SaaS 网站喜欢 HttpOnly Cookie
对于传统 Web SaaS,使用 HttpOnly Cookie 保存登录凭证有一个明显优势:前端 JavaScript 不需要直接读取认证凭证。
浏览器负责在符合规则的请求中发送 Cookie。
这可以降低一部分因为前端代码不慎读取 Token 而导致的泄露风险。
但 HttpOnly 并不等于“防住 XSS”。
如果攻击者已经能够在目标网站上下文执行恶意 JavaScript,他仍然可能利用当前用户的浏览器执行某些经过认证的操作,即使无法直接读取 HttpOnly Cookie。
因此 XSS 防护、输出编码、Content Security Policy、CSRF 防护以及合理的授权检查仍然需要存在。
Cookie 认证为什么需要考虑 CSRF
Session Cookie 或 Cookie 中的 Token 会被浏览器自动发送,因此网站需要考虑 CSRF。
攻击者可能诱导已经登录的网站用户访问一个恶意页面,然后利用浏览器自动携带认证 Cookie 的特点向目标网站发送请求。
SameSite Cookie 属性可以降低一部分 CSRF 风险,但企业仍然应该根据具体认证模式设计 CSRF 防护。
对于修改密码、修改邮箱、转账、删除资源、改变组织权限等状态修改操作,不能因为“已经使用 HTTPS”就认为安全。
HTTPS 解决的是传输过程中的机密性和完整性问题,而不是授权和 CSRF。
SaaS 最大的问题往往不是登录,而是登录之后能干什么
用户成功登录,只能证明身份认证通过。
这并不意味着用户可以访问任何数据。
SaaS 最重要的安全边界之一,是 Authentication 和 Authorization 的分离。
Authentication 回答的是“你是谁”。
Authorization 回答的是“你能访问什么”。
假设一个 SaaS 平台有两个企业租户,Tenant A 和 Tenant B。
用户属于 Tenant A,即使攻击者通过修改 URL 中的对象 ID,尝试访问 Tenant B 的订单、客户资料或者文件,服务器也必须重新验证当前用户是否拥有访问这个具体资源的权限。
不能仅仅因为 JWT 中存在一个 tenant_id,就把客户端传来的对象 ID 当成可信数据。
服务器必须执行对象级授权检查。
这也是多租户 SaaS 与普通单用户网站之间非常重要的区别。
登录系统还必须考虑会话固定和账户恢复
用户成功登录以后,Session ID 不应该继续沿用登录之前可能存在的匿名 Session ID。
服务器通常应该在身份认证成功后轮换 Session ID,以降低 Session Fixation 风险。
注销操作也不能只是让浏览器删除 Cookie。
服务器端 Session 应该失效;如果系统使用 Token,则需要按照 Token 生命周期和撤销策略处理。
密码修改、账户恢复、管理员强制下线以及高风险安全事件,也应该考虑现有会话和 Refresh Token 是否需要全部撤销。
而密码重置流程本身也是身份认证系统的一部分。
如果攻击者可以通过密码重置接口绕过正常认证,那么前面设计得再漂亮的 Session 和 JWT 都没有意义。
企业 SaaS 应该怎么选择
如果是传统的 Web SaaS,浏览器直接访问服务器渲染的页面,Session 加 HttpOnly Cookie 往往是非常成熟的方案。
如果系统是复杂的 API 平台、移动应用、微服务或者需要多个不同客户端访问,那么 Token 架构可能更加合适。
如果浏览器前端和 API 分离,也不意味着一定必须使用 JWT。
OAuth 2.0 主要解决授权框架问题,OpenID Connect 则在 OAuth 2.0 之上提供身份认证层。企业在做 SSO、第三方身份提供商登录和企业身份联邦时,通常需要理解这些协议之间的区别。
对于企业 SaaS,实际架构经常是混合的。
浏览器可能通过 HttpOnly Cookie 保持登录状态,API Gateway 或后端服务再根据会话建立用户身份上下文;微服务之间可能使用短期服务身份凭证;企业 SSO 使用 OIDC;后台高权限操作再要求 MFA 或重新认证。
这种架构比“所有东西统一使用 JWT”更加符合大型 SaaS 的实际需求。
登录状态设计应该关注哪些指标
工程团队评估认证系统时,不能只看登录速度。
需要关注 Session 和 Token 的有效期、撤销能力、并发登录策略、Refresh Token 轮换、密码修改后的会话处理、MFA、异常登录检测、CSRF、XSS、Session Fixation、账户枚举、登录 Rate Limiting、密码重置以及审计日志。
同时还要考虑多租户授权。
一个安全的 SaaS 平台必须在服务器端持续验证租户边界、用户角色和对象权限,而不是把权限信息完全交给浏览器或者客户端决定。
从架构角度看,Cookie、Session 和 Token 并不是三个互相竞争的产品。
Cookie解决的是浏览器如何保存和发送凭证。
Session解决的是服务器如何维护登录状态。
Token解决的是如何携带和验证身份凭证。
JWT只是 Token 的一种标准格式。
Redis则可以成为 Session Store,也可以承担其他缓存和状态管理任务。
真正决定 SaaS 登录系统安全性的,是这些组件组合起来以后形成的认证、会话、授权和撤销机制。
对于企业级 SaaS,比较成熟的设计通常不是追求“完全无状态”或者“完全不用数据库”,而是在安全性、可撤销性、水平扩展、用户体验和运维复杂度之间取得平衡。登录成功只是安全边界的起点,真正决定一个 SaaS 是否可靠的,是用户登录以后,系统能否持续准确地判断身份、控制权限、隔离租户,并在凭证泄露或者账户风险出现时迅速切断访问。
SaaS 登录状态是怎么保存的,Cookie、Session 和 Token 各有什么区别
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP