详解 http 鉴权
前端开发登录鉴权方案完全梳理 🌟🌟🌟
注:此处主要讲的是 http 鉴权涉及 API 鉴权,主要场景是将接口提供给前端或第三方使用时需要鉴权,而非用户登录系统。通常在当前流行的微服务中,主要在 gateway 层做相关认证,下游链路是默认开放的。
概念明确:
- 认证 Identification:根据声明者所持有的识别信息,确认声明者的身份
- 授权 Authorization:在信息安全领域是指资源所有者委派执行者,赋予执行者指定范围的资源操作权限,以便对资源的相关操作。
- 鉴权 Authentication:在信息安全领域是指对于一个声明者所声明的身份权利,对其所声明的真实性进行鉴别确认的过程。
- 权限控制 Access/Permission Control:将操作制成权限列表,然后判断操作允许或禁止
通常四者的关系是:认证 --> 授权 --> 鉴权 --> 权限控制
在不涉及权限控制的系统中,一般到鉴权这一步就能拿到数据。
下面讲集中常用的鉴权方式:
Basic auth
在 header 头中加入 ‘Authorization: Basic dXNlcjE6MTIzNDU2’,其中 dXNlcjE6MTIzNDU2 是 user1:123456 base64 编码之后的结果, 其为用户名密码,这种方式非常简单且极易被破解,仅做了解。
使用方式:
// header
Authorization: Basic dXNlcjE6MTIzNDU2
Session-cookie
概念:
cookie: 服务端下发给浏览器端的数据,浏览器每次发送请求都会携带这段数据
- cookie 存储在客户端,可随意篡改,不安全
- 有大小限制,最大为 4kb
- 有数量限制,一般一个浏览器对于一个网站只能存不超过 20 个 Cookie,浏览器一般只允许存放 300 个 Cookie
- Cookie 是不可跨域的,但是一级域名和二级域名是允许共享使用的(靠的是 domain)
session: 服务端保存生成的 session 信息,客户端在收到 session 信息后会在下次请求时带上 session_id,服务端查找到对应的会话信息来持续之前断掉的会话,cookie 和 session 都弥补了 http 是个无状态协议这一特点。与 cookie 的区别如下:
- 安全性: Cookie 由于保存在客户端,可随意篡改,Session 则不同存储在服务器端,无法伪造,所以 Session 的安全性更高;
- 存取值的类型不同: Cookie 只支持字符串数据,Session 可以存任意数据类型;
- 有效期不同: Cookie 可设置为长时间保持,Session 一般失效时间较短;
- 存储大小不同: Cookie 保存的数据不能超过 4K;
时序图:
使用 session-cookie 鉴权的时序图如下:
- 客户端首次访问(登录),服务端自动创建 session 保存在 redis 或者内存中
- 可以对 sid(session id) 进行加密
- 浏览器解析响应头将 sid 存在 cookie 中,之后的访问都带着它,服务端根据它来鉴权
- 用户登出,客户端和服务端均销毁各自保存的 session id
优势:
- Cookie 简单易用可以在客户端长时间保存
- Session 数据存储在服务端,相较于 JWT 方便进行管理,也就是当用户登录和主动注销,只需要添加删除对应的 Session 就可以了,方便管理
缺点:
- 不安全,Cookie 将数据暴露在浏览器中,增加了数据被盗的风险(容易被 CSRF 等攻击)
- Session 存储在服务端,增大了服务端的开销,用户量大的时候会大大降低服务器性能
- 分布式系统中为了保存 session 需要启用一个 Redis 集群,成本较高
使用方式:
// header
cookie: sessionid=123456
使用场景:
- 一般中大型的网站都适用(除了 APP 移动端);
- 由于一般的 Session 需集中存储在内存服务器上(如 Redis),这样就会增加服务器的预算,所以预算不够请谨慎选择
token
时序图:
使用 token 鉴权的时序图
和 session-cookie 的区别在于:token 是 session-cookie 的升级版
- token 在服务端不存库,每次通过定义的规则校验是否匹配合法即可
- token 在客户端存在 localStorage 或 sessionStorage 中,因此不受限于 cookie 的缺点,比如大小受限、native app 不能使用 cookie、浏览器中 cookie 存储功能被禁用、跨域影响等
- 客户端再发请求时直接放在头部 Authorization 字段中发送
- 更灵活比如 JWT 等
优点:
- 服务端无状态,token 不必再存储在服务端
- 支持移动设备,支持跨域,有效避免 csrf 攻击因为不需要用 cookie。但是会存在 XSS 攻击中被盗的风险,但是可选择 Token 存储在标记为 httpOnly 的 Cookie 中,能够有效避免浏览器中的 JS 脚本对 Cookie 的修改
缺点:
- 占带宽:正常情况下比 sid 更大,消耗更多流量,挤占更多宽带
- 性能问题:相比较于 Session-Cookie 认证来说,Token 需要服务端花费更多时间和性能来对 Token 进行解密验证,其实 Token 相较于 Session—Cookie 来说就是一个时间换空间的方案
- 有效期问题:为了避免 Token 被盗用,一般 Token 的有效期会设置的较短,所以就有了 Refresh Token;
refresh token
概念
refresh token 的概念和 access token 密切相关
Access Token: 用来访问业务接口,由于有效期足够短,盗用风险小,也可以使请求方式更宽松灵活;
Refresh Token: 用来获取 Access Token,有效期可以长一些,通过独立服务和严格的请求方式增加安全性;由于不常验证,也可以如前面的 Session 一样处理;一般如果 refresh Token 也过期了则需要重新登录。
JWT json web token
概念:
对 JSON 进行加密签名来实现授权验证的方案
组成:
- header 头部
- typ token 类型
- alg 使用的 hash 算法,通常是 hmac sha256 或 rsa
{
"alg": "HS256",
"typ": "JWT"
}
-
Payload: claim 即通常包含 user 信息以及实际需要传递的数据,官方定义了如下七个字段供使用,但也可以自己加私有字段
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
{ "iss": "Jehoshaphat Tse", "iat": 1441593502, "exp": 1441594722, "aud": "www.example.com", "sub": "mrsingsing@example.com", "name": "John Doe", "admin": true }
-
signature 签名,对前面两部分进行签名,防止数据被篡改
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
其中 secret 是一个密钥,只有服务器知道,不会泄露给用户,使用的签名算法是 header 中指定的,默认即 HMAC SHA256
优势:
- 不需要在服务端保存会话信息,所以易于应用的扩展
- JWT 中的 Payload 负载可以存储常用信息,用于信息交换,有效地使用 JWT,可以降低服务端查询数据库的次数
缺点:
- 时效问题:由于服务端不存储状态,所以一旦 jwt 签发了就不能在使用过程中废止。
- 安全性:由于 jwt 的 payload 是 base64 编码的没有进行加密,所以不能存储敏感数据但也可以通过加密来解决这个问题
- 性能问题:jwt 放在 header 中,占用空间大导致 header 可能比 body 还大
时序图:
使用方式:
// header
Authorization: Bearer <token>
通常将 jwt 存储在 localStorage 中
使用场景:
不需要服务端保存用户状态的场景。但通常如果考虑到 token 注销的场景的话,没有特别好的解决方案,大部分解决方案是给 token 加状态,比如为每个用户创建专属 secret 有点类似于 session-cookie 认证了,对于续签的问题可以通过每次请求都返回新的 token 的方式或即将过期的时候服务端即重新颁发新的给客户端。
总结 jwt 的做法其实我们可以借鉴做一下 jwt 的变形,比如 header 中使用如下验证方式:
// header
// ts 用于校验时间
// sign 用于保证数据不被篡改
// 发送给服务端后对比签名是否一样(需要客户端和服务端都知道密钥才能按相同的算法生成签名)
timestamp+signature(secret, payload)
SSO single sign-on
概念:
多系统应用群中登录单个系统即可在其他系统中获得授权无需再次登录其他系统即可使用。
同域 SSO
当存在两个相同域名下的系统 A a.abc.com
和系统 B b.abc.com
时,以下为他们实现 SSO 的步骤:
- 用户访问某个子系统时(例如
a.abc.com
),如果没有登录,则跳转至 SSO 认证中心提供的登录页面进行登录 - 登录认证后,服务端把登录用户的信息存储于 Session 中,并生成会话身份凭证追加在响应头的
set-cookie
字段中,随着请求返回写入浏览器中。 - 下次发送请求时,当用户访问同域名的系统 B 时,由于 A 和 B 在相同域名下,也是
abc.com
,浏览器会自动带上之前的 Cookie。此时服务端就可以通过该 Cookie 来验证登录状态了,注意防 xss 需要设置 cookie 为 http-only。
这实际上使用的就是 Session-Cookie 认证的登录方式。
跨域 SSO
由于跨域,cookie 不能共享了,比如我们登录了 taobao.com 希望同时能登上 tmall.com
saml 协议
SAML Web SSO学习 🌟🌟🌟
概念:
saml security assertion markup language: 安全断言标记语言
SP service provider:服务提供方,如阿里云控制台、aws 控制台等
IdP identity provider: 身份提供方,能够向 SP 发送身份断言,即标识某个人身份的 token,且在 saml 协议中其为 xml 形式
通常 SP 和 IdP 互相存着对方的 MetaData
组成:
- assertions 断言:包含 authentication、attribute、entitlement information
- protocols 协议
- bindings
- profiles
- MetaData
- IdP 发出 saml response 之前会在 MetaData 中查询受信 sp 列表,并发向其定义的 endpoint
- SP 根据 IdP MetaData 里的公钥验证断言上的签名
- SP 从 MetaData 查找 IdP 的组件服务的预先安排的 endpoint
时序图:
OAuth2.0
- TODO 待补充
ak sk
常用于云服务中
简介
AK: Access Key Id,用于标示用户;
SK: Secret Access Key,是用户用于加密认证字符串和服务端用来验证该字符串的密钥,因此 SK 必须保密;
综上 aksk 其实是通过使用 Access Key Id / Secret Access Key 加密的方法来验证某个请求的发送者身份。
使用流程
判断用户请求中是否包含 Authorization 认证字符串。如果包含认证字符串,则执行下一步操作。
基于HTTP请求信息,使用相同的算法,生成Signature字符串。
使用服务器生成的Signature字符串与用户提供的字符串进行比对,如果内容不一致,则认为认证失败,拒绝该请求;如果内容一致,则表示认证成功,系统将按照用户的请求内容进行操作。
客户端:
1. 构建http请求(包含 access key);
2. 使用请求内容和 secret access key 计算的签名(signature);
3. 发送请求到服务端;
服务端:
1. 根据发送的 access key 查找数据库得到对应的 secret-key;
2. 使用同样的算法将请求内容和 secret-key 一起计算签名(signature),与客户端步骤2相同;
3. 对比用户发送的签名和服务端计算的签名,两者相同则认证通过,否则失败。