一、什么是鉴权?为什么要鉴权
鉴权(authentication),也叫做认证,即验证用户是否拥有访问系统的权利。
HTTP本身是无状态的请求,每次请求都是一次性的,并不会知道请求前后发生了什么。但在很多情况情况下都需要维护状态,最典型的就是用户登录系统,并在系统中进行一系列操作
API接口直接暴露在互联网上是存在安全风险的,如果不进行鉴权,就有可能被网上的不法分子恶意攻击(如:爬虫,恶意访问等),使得api资源被非法访问占用,正常用户访问受阻。因此鉴权十分必要,只有鉴权成功的请求才能请求服务,否则就被拦截
二、Cookie/Session鉴权
为每个用户颁发一个身份牌
为了识别用户,可以为每个用户设置一个“身份牌”,“身份牌”里面可以存放用户的信息,每次请求的时候都带上这个“身份牌”,这样就可以用来识别用户了。而这个“身份牌”就是我们经常提到的“cookie”
Session会话的引入
随着web应用的告诉发展,越来越多的用户信息需要存储,如果仅仅依靠Cookie字段存储,会大大增加每次请问的负担。
因此引入会话(session)的概念,使用一个会话ID(sessionID)映射到用户的各种信息上,每次请求只需要在cookie携带上sessionID就可以解决cookie增长的问题
为了更好的负载均衡与容灾,后端服务器一般使用分布式方式部署,一个用户的Session信息在单台服务器上创建后需要同步给其余的服务器。因此Session映射采用Redis等缓存服务器方式才能解决跨地域多机房问题
(为了负载均衡与容灾,Redis集群也需要使用多机房部署)
局限
- 依赖cookie,cookie可以被禁用,也可以被劫持篡改
- 移动端中cookie和session机制不被支持,跨平台不好兼容
- 缓存服务器的多机房信息同步需要时间,如果服务某个机房出现问题,依然可能造成用户Session短暂失败
二、token授权之JWT
分散压力
既然基于cookie的session鉴权存在上述问题,那么能否改变一下思路,跳出以上的局限性。
Session会话机制的痛点在于,Session的用户解析需要后端服务来维护逻辑,甚至还需要一个缓存服务器来维护,这使得一次请求的链路变长,出现问题的概率也比较大
那干脆让客户端来维护自己的用户信息就好了,这样大量的用户信息就分散到各个用户的设备上,服务器的压力就会减少很多
客户维护鉴权信息的机制类似于http协议也是无状态的,这也使得整个服务拓展性大大提升
用token代替session
- 用户第一次登录,服务器验证用户,通过后生成一个token
- 在第一次返回时将token返回给客户端,客户端存储下来,下次请求时携带上该token
- 之后只要验证token是否有效即可
- 为了避免篡改,可以在token中使用加密算法生成签名,加入token(私钥保存在服务器上,因此签名无法被破解)
JWT(JSON Web Token)token生成标准
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。因为携带了数字签名,因此该信息可以被验证和信任的
JWT由三部分数据组成,三部分由 点(.) 进行分割
- Header(头部)
- Payload(负载)
- Signature(签名)
Header.Payload.Signature
Header
Header 部分是一个 JSON 对象,描述 JWT 的元数据,例如:
{
"alg": "HS256",
"typ": "JWT"
}
alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT
最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串
Payload
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,可以从中选择使用
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
当然也可以自定义字段,不过由于这部分数据是明文传送,所以不要在payload字段上存放任何私密信息
Signature
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户
因为使用了签名,因此即使泄露,也无法被篡改
局限
- 服务器不保存Session信息,在没有特殊逻辑的情况下,无法在使用过程中废止某个 token,也不能更改 token 的权限,一旦 JWT 签发了,在到期之前就会始终有效
- Token本身就是认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证
- 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输
三、token授权之OAuth2.0
严格来讲,OAuth2.0不算一种授权方式,而是一个安全的授权认证的框架。为系统中不同角色、用户、服务前端应用(比如API),以及客户端(比如网站或移动App)提供了一套相互认证的框架
所以一般而言,在OAuth2.0中存在三种角色:服务提供方,用户,第三方服务 ;用户授权第三方服务使用自身的某些权限,去访问服务提供方的资源(比如用qq账户登录微博)
OAuth2.0分为两步,获取Authorization以及获取Token
获取Authorization
- 用户在授权界面,选择需要授权的权限点,登录确定授权
- 授权后回调第三方服务的回调地址
- 通过回调地址,发送Authorization(可以看做是验证码,有效期不长)给第三方服务
获取Token并维护Token有效性
- 通过上面拿到的Authorization调用服务提供方的接口获取token
- token由access token与refresh token两个token组成,access token有效性比refresh token短
- 访问服务提供方接口需要使用access token,而当access token快过期的时候就需要用refresh token请求token刷新接口,获取新的token
OAuth 2.0 rfc文档:https://www.rfc-editor.org/rfc/rfc6749
OAuth2.0提供了4种认证方式,上面是最为复杂的一种,授权码模式,其余3种授权方式如下
简化模式
不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证
(A)客户端将用户导向认证服务器。
(B)用户决定是否给于客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F)浏览器执行上一步获得的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。
密码模式
用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式
(A)用户向客户端提供用户名和密码。
(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。
(C)认证服务器确认无误后,向客户端提供访问令牌。
客户端模式
指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证
(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。
(B)认证服务器确认无误后,向客户端提供访问令牌。
优点
- 更安全,客户端不接触用户密码
- 资源服务器可以和授权服务器解耦
- 适用多种客户端架构场景
缺点
- 协议框架太宽泛,每个服务又都自己的实现,兼容性和互操作性比较差
- 客户端需要维护token有效性,维护定时刷新token任务
四、ak/sk认证鉴权
- 服务提供方需要给每个用户分配一对 Access Key Id(AK) / Secret Access Key(SK)
- 双方约定同样的加密算法,利用 时间戳,sk,nonce(一个随机值,为了避免请求重放),参数等信息作为计算因子,算出sign签名
- 用户在调用api时,需要提供自己的身份认证(AK),时间戳、nonce以及加密好的sign签名
- 服务提供方验证签名是否过期,验证nonce是否唯一,并利用同样的算法算出sign,与传入sign进行比对,如果一致则通过认证
优点
- 安全性高,在秘钥与算法不泄露情况下,签名无法破解
- 有过期时间,可以避免签名长期有效的情况
- 有防止重放机制,即使签名被捕获,也无法再次利用相同的签名进行请求
缺点
- 对于服务调用方与被调用方都需要一定的编码要求
- 每次请求都需要重新计算签名,不那么轻量
- 服务提供方需要维护ak/sk关系,并且需要维护nonce是否唯一逻辑