鉴权也叫身份验证(Authentication),是指验证用户是否拥有访问系统的权利。
Session-Cookie 认证
Session-Cookie
认证是利用服务端的 Session(会话)和浏览器(客户端)的 Cookie 来实现的前后端通信认证模式。
由于 HTTP 请求时是无状态的,服务端正常情况下无法得知请求发送者的身份,这个时候我们如果要记录状态,就需要在服务端创建 Session 会话,将相同客户端的请求都维护在各自的会话记录中,每当请求到达服务端时,先校验请求中的用户标识是否存在于 Session 中,如果有则表示已经认证成功,否则表示认证失败。
Cookie 主要用于以下三个方面:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其他需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为追踪(如跟踪分析用户行为等)
下图为 Session-Cookie 认证的工作流程图:
- 服务端在接收到来自客户端的首次访问时,会自动创建 Session(将 Session 保存在内存中,也可以保存在 Redis 中),然后给这个 Session 生成一个唯一的标识字符串会话身份凭证
session_id
(通常称为sid
),并在响应头Set-Cookie
中设置这个唯一标识符。 - 浏览器收到请求响应后会解析响应头,并自动将
sid
保存在本地 Cookie 中,浏览器在下次 HTTP 请求时请求头会自动附带上该域名下的 Cookie 信息 - 服务端在接收客户端请求时会去解析请求头 Cookie 中的
sid
,然后根据这个sid
去找服务端保存的该客户端的sid
,然后判断该请求是否合法 - 一旦用户登出,服务端和浏览器将会同时销毁各自保存的会话 ID,服务端会根据数据库验证会话身份凭证,如果验证通过,则继续处理
优点:
- Cookie 简单易用,在不受用户干预或过期处理的情况下,Cookie 通常是客户端上持续时间最长的数据保留形式
- Session 数据存储在服务端,相较于 JWT 方便进行管理,也就是当用户登录和主动注销,只需要添加删除对应的 Session 就可以了,方便管理
缺点:
- 非常不安全,Cookie 将数据暴露在浏览器中,增加了数据被盗的风险(容易被 CSRF 等攻击)
- Session 存储在服务端,增大了服务端的开销,用户量大的时候会大大降低服务器性能
- 用户认证后,服务端做认证记录,如果认证的记录被保存在内存中,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权资源,这样在分布式的应用上,相应的限制了负载均衡的能力,也意味着限制了应用的扩展能力
Token 认证
Token 和 Session-Cookie 认证方式中的 Session ID 不同,并非只是一个标识符。Token 一般会包含 用户的相关信息
,通过验证 Token 不仅可以完成身份校验,还可以获取预设的信息。
基于 Token 的身份验证方法:
- 用户输入登录信息并请求登录
- 服务端收到请求,验证用户输入的登录信息
- 验证成功后,服务端会
签发
一个 Token(通常包含用户基础信息、权限范围和有效时间等),并把这个 Token 返回给客户端 - 客户端收到 Token 后需要把它存储起来,一般会放在 localStorage 里(一般不放 Cookie 因为可能会有跨域问题,以及安全性问题)
- 后续客户端每次向服务端请求资源的时候,将 Token 附带于 HTTP 请求头 Authorization 字段中发送请求
- 服务端收到请求后,去校验客户端请求中 Token,如果验证成功,就向客户端返回请求的数据,否则拒绝返还
优点:
- 服务端无状态:Token 机制在服务端不需要存储会话(Session)信息,因为 Token 自身包含了其所标识用户的相关信息,这有利于在多个服务间共享用户状态
- 性能相对较好:因为在验证 Token 时不用再去访问数据库或远程服务进行权限校验,自然可以提升不少性能
- 支持移动设备
- 支持跨域跨程序调用,因为
Cookie 是不允许跨域访问
的,而 Token 则不存在这个问题 - 有效避免 CSRF 攻击(因为不需要 Cookie),但是会存在 XSS 攻击中被盗的风险,但是可选择 Token 存储在标记为
httpOnly
的 Cookie 中,能够有效避免浏览器中的 JS 脚本对 Cookie 的修改
缺点:
- 占带宽:正常情况下比
sid
更大,消耗更多流量,挤占更多宽带 - 性能问题:相比较于 Session-Cookie 认证来说,Token 需要服务端花费更多时间和性能来对 Token 进行解密验证,其实 Token 相较于 Session—Cookie 来说就是一个时间换空间的方案
Token 生成方式
最常见的 Token
生成方式是使用 JWT
(Json Web Token
),它是一种简洁的、自包含的方法,用于通信双方之间以 JSON 对象的形式安全的传递信息。
使用 Token
后,服务器端并不会存储 Token
,那怎么判断客户端发过来的 Token
是合法有效的呢?
答案其实就在 Token
字符串中,其实 Token
并不是一串杂乱无章的字符串,而是通过多种算法拼接组合而成的字符串。
JWT
算法主要分为 3 个部分:header
(头信息),playload
(消息体),signature
(签名)。
header
部分指定了该JWT
使用的签名算法;playload
部分表明了JWT
的意图;signature
部分为JWT
的签名,主要为了让JWT
不能被随意篡改。
关于 JWT
,这里简单说明一下,具体细节大家可以去看一下 JWT 官网。
或者参考阮一峰的这篇文章:JSON Web Token 入门教程
Session-Cookie 认证和 Token 认证的比较
Session-Cookie 认证和 Token 认证有很多类似的地方,但是 Token 认证更像是 Session-Cookie 认证的升级改良版。
1、使用 Token,服务端不需要保存状态. 在 session 中 sessionid 是一个唯一标识的字符串,服务端是根据这个字符串,来查询在服务器端保持的 session,这里面才保存着用户的登陆状态。但是 token 本身就是一种登陆成功凭证,他是在登陆成功后根据某种规则生成的一种信息凭证,他里面本身就保存着用户的登陆状态。服务器端只需要根据定义的规则校验这个 token 是否合法就行。
2、Token 验证机制丰富了客户端类型。session-cookie 是需要 cookie 配合的,那么在 http 代理客户端的选择上就只有浏览器了,因为只有浏览器才会去解析请求响应头里面的 cookie,然后每次请求再默认带上该域名下的 cookie。但是我们知道 http 代理客户端不只有浏览器,还有原生 APP 等等,这个时候 cookie 是不起作用的,或者浏览器端是可以禁止 cookie 的,但是 token 就不一样,他是登陆请求在登陆成功后再请求响应体中返回的信息,客户端在收到响应的时候,可以把他存在本地的 cookie,storage,或者内存中,然后再下一次请求的请求头重带上这个 token 就行了。简单点来说 cookie-session 机制他限制了客户端的类型,而 token 验证机制丰富了客户端类型。
3、时效性。session-cookie 的 sessionid 实在登陆的时候生成的而且在登出事时一直不变的,在一定程度上安全就会低,而 token 是可以在一段时间内动态改变的。
4、可扩展性。token 验证本身是比较灵活的,一是 token 的解决方案有许多,常用的是 JWT,二来我们可以基于 token 验证机制,专门做一个鉴权服务,用它向多个服务的请求进行统一鉴权。