微服务权限
什么是Authentication(认证)
和Authorization(鉴权)
:认证关心你是谁,鉴权关心你能干什么。
微服务权限,往小拆分,分为三块:用户认证
、用户权限
、服务校验
。
用户认证
传统的单体应用可能习惯了session的存在,而到了Spring Cloud的微服务化后,session虽然可以采取分布式会话来解决,但终究不是上上策。开始有人推行Spring Cloud Security结合很好的OAuth2,后面为了优化OAuth 2中Access Token的存储问题,提高后端服务的可用性和扩展性,有了更好Token验证方式JWT(JSON Web Token)。这里要强调一点的是,OAuth2和JWT这两个根本没有可比性,是两个完全不同的东西。
OAuth2是一种授权框架,而JWT是一种认证协议。
OAUth2认证框架
OAuth2中包含四个角色:
- 资源拥有者(Resource Owner)
- 资源服务器(Resource Server)
- 授权服务器(Authorization Server)
- 客户端(Client)
OAuth2包含4种授权模式:
- 授权码(认证码)模式 (Authorization code)
- 简化(隐形)模式
- 用户名密码模式 (Resource Owner Password Credential)
- 客户端模式 (Client Credential)
其中,OAuth2的运行流程如下图,摘自RFC 6749:
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
在Spring Cloud OAuth2中,所有访问微服务资源的请求都在Http Header中携带Token,被访问的服务接下来再去请求授权服务器验证Token的有效性,目前这种方式,我们需要两次或者更多次的请求,所有的Token有效性校验都落在的授权服务器上,对于我们系统的水平扩展成为一个非常大的瓶颈。
JWT(JSON Web Tokens)认证协议
JWT: 生成并发给客户端之后,后台是不用存储,客户端访问时会验证其签名、过期时间等再取出里面的信息(如username),再使用该信息直接查询用户信息完成登录验证。jwt自带签名、过期等校验,后台不用存储,缺陷是一旦下发,服务后台无法拒绝携带该jwt的请求(如踢除用户);
Token+Redis: 是自己生成个32位的key,value为用户信息,访问时判断redis里是否有该token,如果有,则加载该用户信息完成登录。服务需要存储下发的每个token及对应的value,维持其过期时间,好处是随时可以删除某个token,阻断该token继续使用。
JWT比Redis+Token模式更加容易扩展,是完全依赖算法的认证方式,不依赖于Session,服务端也无需存储。
JWT应用在庞大的系统 或者 各自独立的系统 或者 不同人开发的系统
Token+Redis应用在中小的系统里 或者 各系统连系紧密 或者 同一批人开发的系统
协议格式
JSON Web Tokens的格式组成,jwt是一段被base64编码过的字符序列,用点号分隔,一共由三部分组成,头部header,消息体playload和签名sign。
1.jwt的头部Header是json格式:
{
"typ":"JWT",
"alg":"HS256",
"exp":1491066992916
}
其中typ是type的简写,代表该类型是JWT类型,加密方式声明是HS256,exp代表当前时间。
2.jwt的消息体Playload
{
"userid":"123456",
"iss":"companyName"
}
消息体的具体字段可根据业务需要自行定义和添加,只需在解密的时候注意拿字段的key值获取value。
3.签名sign的生成
最后是签名,签名的生成是把header和playload分别使用base64url编码,接着用’.‘把两个编码后的字符串连接起来,再把这拼接起来的字符串配合**进行HMAC SHA-256算法加密,最后再次base64编码下,这就拿到了签名sign. 最后把header和playload和sign用’.‘ 连接起来就生成了整个JWT。
认证流程
Token存储会很大影响我们系统的可扩展性,这是我们引入JWT的原因。
+-----------+ +-------------+
| | 1-Request Authorization | |
| |------------------------------------>| |
| | grant_type&username&password | |--+
| | |Authorization| | 2-Gen
| Client | |Service | | JWT
| | 3-Response Authorization | |<-+
| |<------------------------------------| Private Key |
| | access_token / refresh_token | |
| | token_type / expire_in / jti | |
+-----------+ +-------------+
授权服务器
将用户信息和授权范围序列化后放入一个JSON字符串,然后使用Base64进行编码,最终在授权服务器用私钥对这个字符串进行签名,得到一个JSON Web Token
。我们可以像使用Access Token
一样的直接使用它,假设其他所有的资源服务器都将持有一个RSA公钥,当资源服务器接收到这个在Http Header中存有Token的请求,资源服务器就可以拿到这个Token,并验证它是否使用正确的私钥签名(是否经过授权服务器签名,也就是验签
)。验签通过,反序列化后就拿到Toekn中包含的有效验证信息。
资源服务器返回的信息可以使以下内容:
- access_token-访问令牌,用于资源访问
- refresh_token-当访问令牌失效,使用这个令牌重新获取访问令牌
- token_type-令牌类型,这里是Bearer也就是基本HTTP认证
- expire_in-过期时间
- jti-JWT ID
使用JWT可以简单的传输Token
,用RSA签名
保证Token
很难被伪造。Access Token
字符串中包含用户信息和权限范围,我们所需的全部信息都有了,所以不需要维护Token
存储,资源服务器也不必要求Token
检查。
+-----------+ +-----------+
| | 1-Request Resource | |
| |----------------------------------->| |
| | Authorization: bearer Access Token | |--+
| | | Resource | | 2-Verify
| Client | | Service | | Token
| | 3-Response Resource | |<-+
| |<-----------------------------------| Public Key|
| | | |
+-----------+ +-----------+
所以,在微服务中使用OAuth 2,不会影响到整体架构的可扩展性。
用户权限
传统的单体应用的权限拦截,大家都喜欢shiro,而且用的颇为顺手。可是一旦拆分后,这权限开始分散在各个API了,shiro还好使吗?前后端分离后,交互都是token,后端的服务无状态化,前端按钮资源化,权限放哪儿管好使?
抽象与设计
在介绍灵活的核心设计前,先给大家普及一个入门的概念:RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。
RBAC其实是一种分析模型,主要分为:基本模型RBAC0(Core RBAC)、角色分层模型RBAC1(Hierarchal RBAC)、角色限制模型RBAC2(Constraint RBAC)和统一模型RBAC3(Combines RBAC)。
服务检验
上一篇: 前端学习第5天