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