统一认证与授权是微服务架构的基础功能,微服务架构不同于单体应用的架构,认证和授权非常集中。当服务拆分之后,对各个微服务认证与授权变得非常分散,所以在微服务架构中,将集成统一认证与授权的功能,作为横切关注点。
常见的认证与授权方案有 OAuth、分布式 Session、OpenID 和 JWT 等
相关理论的介绍主要来自于OAuth2官方文档,相关地址为https://tools.ietf.org/html/rfc6749。
OAuth2是当前授权的行业标准,其重点在于为Web应用程序、桌面应用程序、移动设备以及室内设备的授权流程提供简单的客户端开发方式。它为第三方应用提供对HTTP服务的有限访问,既可以是资源拥有者通过授权允许第三方应用获取HTTP服务,也可以是第三方以自己的名义获取访问权限。
OAuth2 中主要分为了4种角色
在很多时候,资源服务器和授权服务器是合二为一的,在授权交互的时候是授权服务器,在请求资源交互是资源服务器。但是授权服务器是单独的实体,它可以发出被多个资源服务器接受的访问令牌。
官方流程图
+--------+ +---------------+
| |--(1)- Authorization Request ->| Resource |
| | | Owner |
| |<-(2)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(3)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(4)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(5)----- Access Token ------>| Resource |
| | | Server |
| |<-(6)--- Protected Resource ---| |
+--------+ +---------------+
流程主要包含以下的6个步骤
客户端授权类型
为了获取访问令牌,客户端必须获取到资源所有者的授权许可。OAuth2默认定了四种授权类型,当然也提供了用于定义额外的授权类型的扩展机制。默认的四种授权类型为:
下面对常用的授权码类型和密码类型进行详细的介绍。
授权码类型
授权码类型(authorization code)通过重定向的方式让资源所有者直接与授权服务器进行交互来进行授权,避免了资源所有者信息泄漏给客户端,是功能最完整、流程最严密的授权类型,但是需要客户端必须能与资源所有者的代理(通常是Web浏览器)进行交互,和可从授权服务器中接受请求(重定向给予授权码),授权流程如下:
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(2)
+----|-----+ Client Identifier +---------------+
| -+----(1)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(2)-- User authenticates --->| Server |
| | | |
| -+----(3)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(1) (3) | |
| | | |
^ v | |
+---------+ | |
| |>---(4)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(5)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
————————————————
1.客户端引导资源所有者的用户代理到授权服务器的endpoint,一般通过重定向的方式。客户端提交的信息应包含客户端标识(client identifier)、请求范围(requested scope)、本地状态(local state)和用于返回授权码的重定向地址(redirection URI);
2.授权服务器认证资源所有者(通过用户代理),并确认资源所有者允许还是拒绝客户端的访问请求;
3.如果资源所有者授予客户端访问权限,授权服务器通过重定向用户代理的方式回调客户端提供的重定向地址,并在重定向地址中添加授权码和客户端先前提供的任何本地状态;
4.客户端携带上一步获得的授权码向授权服务器请求访问令牌。在这一步中授权码和客户端都要被授权服务器进行认证。客户端需要提交用于获取授权码的重定向地址;
5.授权服务器对客户端进行身份验证,和认证授权码,确保接收到的重定向地址与第三步中用于的获取授权码的重定向地址相匹配。如果有效,返回访问令牌,以及可能返回的刷新令牌(Refresh Token)。
密码类型
JWT(JSON Web Token)作为一个开放的标准,通过紧凑(Compact)或者自包含(Self-contained)的方式,定义了用于在各方之间发送的安全JSON对象。
JWT可以很好地充当在上面介绍的访问令牌和刷新令牌的载体,这是Web双方之间进行安全传输信息的良好方式。只有授权服务器持有签发和验证JWT的密钥,那么就只有授权服务器能验证JWT的有效性以及发送带有签名的JWT,这就唯一保证了以JWT为载体的令牌的有效性和安全性。
它由三部分组成,每部分通过.分隔开,分别是:
Header(头部)
Payload(有效负荷)
Signature(签名)
头部通常由两部分组成:
·typ:类型,一般为JWT。
·alg:加密算法,通常是HMAC SHA256或者RSA。
一个简单的头部例子如下所示:
{
“alg”: “HS256”
“typ”: “JWT”
}
这部分JSON会由Base64Url编码,构成JWT的第一部分,如下所示:
R5cCI6IJ9eyJh2IV7Hy6IkpX5cCIJ9
一般不建议在Payload中添加任何敏感信息,因为Base64是对称解密的,这意味着Payload中的信息是可见的。
一个简单的Payload例子如下所示:
{
"name": "enzo",
"exp": 1568287607,
"userId": "00001"
}
这部分JSON会由Base64Url编码,构成JWT的第二部分:
B29umiY1MTEZy2FuIiMTIzNDU2In0mV4cSeyCI6MTUxODAJuYW1lIIs1NywidXNlcklkIjo
要创建签名,需要编码后的头部、编码后的Payload、一个密钥,最后通过在头部alg键值定义的加密算法加密生成签名,生成签名的伪代码如下所示:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
上述代码中用到的加密算法为HMACSHA256。密钥保存在服务端用于验证JWT以及签发JWT,所以必须只由服务端持有,不该泄露出去。
一个简单的签名如下所示:
MnK90MjwMpp0acCi9Pk6m07971I2vcc6y7ki2PQcs
这将成为JWT的第三部分。
这三部分通过“.”分割,组成最终的JWT,如下所示:
R5cCI6IJ9eyJh2IV7Hy6IkpX5cCIJ9.B29umiY1MTEZy2FuIiMTIzNDU2In0mV4cSeyCI6MTUxODAJuYW1lIIs1NywidXNlcklkIjo.MnK90MjwMpp0acCi9Pk6m07971I2vcc6y7ki2PQcs