Spring Cloud 微服务实践 (4) - OAuth2
本文的微服务实践,尝试着用Spring Cloud Security来给微服务增加访问控制,确保各个微服务提供的服务都是安全可控的。主要涉及的内容是Spring Security OAuth2,JSON Web Token和RBAC(基于角色的访问控制)。
本文的代码:https://github.com/xiaoboey/from-zero-to-n/tree/master/two ,Spring Boot的版本是2.3.3,Spring Cloud的版本是Hoxton.SR8。
在前面的几篇文章中,我们已经完成了一个基本的微服务架构。服务既可以通过网关(Gateway)暴露给外部调用,也可以在内部借助Feign提供的负载均衡、重试和熔断等能力,进行服务间的优雅调用,可以说已经具备了一个“雏形”。
接下来我们开始做一个非常重要的“实践”,就是对系统的数据或者说资源进行访问控制。个人认为,讲软件框架和项目实战而不说数据的访问权限控制,都是“耍流氓”。
1、资源和权限控制
平常的概念中,可能说“数据”的时候我们更多的是指数据库中的表或者记录这种结构性数据,而图片、文件等则归于“资源”的范畴。在互联网的术语中,比如URL(Uniform Resource Locator,统一资源定位系统),则把一个网页、一张图片、一个API都定义为资源。所以资源是一个更宽泛的说法,一个系统向外部暴露提供出来的任何东西都可以叫资源:网页、图片、音频、视频、其他文件、API等。
对资源进行访问控制就是用户对某个资源的访问(增删改查)需要具备相应的权限。
目前主流的权限管理模型是RBAC模型(Role-Based Access Control 基于角色的访问控制),权限与角色相关联,用户通过成为适当的角色而被授予这些角色关联的权限。RBAC模型跟生活中的实际情况比较吻合,容易理解,人们在各种场景下都是“角色化生存”,一旦不按“角色”办事就乱套。孔子的“君君臣臣父父子子”,就是说的要有你被“赋予”的角色的样子,当父亲的要有当父亲的样子,当儿子要有当儿子的样子。在微服务的世界里不需要社会舆论和暴力机构来维护次序,只需要集成像Spring Cloud Security这样的一套安全组件向未授权的访问Say No就可以了。
2、Spring Cloud Security和OAuth2
Spring Cloud Security是从Spring Security发展而来,针对微服务的特点提供了安全控制的一整套方案。
微服务把业务拆分得很细,各个细粒度的服务独立部署,并且弹性扩容动态伸缩。所以原来单体应用Spring Security那一套已经不适合微服务了,注意是“不适合”而不是“不能”,因为你要坚持在每个微服务里都去实现RBAC逻辑进行权限控制,也没人拦得住你。
OAuth2是一个用户验证和授权标准,解决的是如何把用户在某个系统里的数据开放给第三方应用访问的问题,用户只需要授权而不需要提供账号和密码,所以OAuth是“开放授权”的意思。
从OAuth2的名词解释来分析,我们大致知道Spring Cloud Security为什么要提供OAuth2实现了:把微服务系统中的各个微服务都看作是独立的软件系统,借助OAuth2的开放授权机制,向使用者(第三方)提供受控的服务。
在OAuth2开放授权的标准里,使用认证中心(AuthorizationServer)进行用户的认证和授权,其他向外部提供服务的资源服务器(ResourceServer)则配合认证中心进行资源的访问控制(鉴权)。
3、JSON Web Token(JWT)
在单体应用中使用会话(Session)来维持用户的登录状态,而微服务是独立部署并且可以相互调用,微服务怎么来记住用户的登录状态呢?事实上也有不少的方案,比如Spring Session是专门解决Session共享的,内存数据库Redis早期也是拿来干Session共享的活,还可以把Session数据持久化存数据库,通过数据库来共享Session。
JSON Web Token(JWT)是跟微服务比较配的一套方案,特别是既有PC Web端也有App、小程序、公众号的系统。JSON Web Token从名称上看,已经包含了大部分信息,JSON是数据格式,Web表示在网络环境传输,Token即令牌。令牌是一个很古老的安全机制,比如说古代的信物,深闺中的小姐要托人传消息给某位公子,就要附带一个信物增加可信度。“信物”的选择需要费一些心思,既要有代表性(唯一性)又要对方能识别。在我们的小姐传书案例中,可能就是小姐的那独特(蹩脚)绣法的一方手帕,而在微服务中呢就是JWT。
JWT令牌是一段Base64编码的数据,主要包括用户账号、角色和过期时间等信息。用Base64编码是为了方便传输。另外为了防止传输过程中被纂改,JWT还包含对数据的签名。
用户登录系统时,通过用户认证(账号密码或者其他方式)后生成令牌,然后这个令牌跟随用户在网络上传递,业务系统拿到用户的资源请求和令牌后,先验证令牌的签名判断令牌的有效性,是否过期,再判断用户是否具备相应的权限,最后才是具体的业务处理。
JWT的优点:
自带数据,所以JWT很适合像微服务这样的分布式独立部署场景,甚至跨域传输;
JWT也跟HTTP的无状态很吻合,服务端接受到请求后,就可以基于令牌携带的信息进行权限控制,而不像Session模式还要访问数据库拿用户的角色权限数据,减少了数据库的访问。
JWT的缺点也是自带数据,真是“成也萧何败也萧何”:
自带数据很显然增加了网络传输量影响了性能,使用JWT的系统要有意识控制令牌包含的信息量;
JWT无法废弃,也就是令牌签发后,直到它过期,这中间是没法把它作废的;
JWT数据是用Base64编码,在网络上传输容易泄露用户信息,所以JWT中不要包含敏感数据,建议就用户ID和角色就可以了,并且启用HTTPS传输数据。
关于JWT无法废弃这一点,可能也有同学想到了可以把废弃的令牌放数据库或者Redis,然后验证JWT令牌时查询一下是不是已经被废弃,这个方案是可行的,但把JWT的优点也给干掉了,还不如直接把Session数据持久化存数据库那个方案。
4、用OAuth2和JWT进行权限控制
前面讲的是理论,现在开始“实践”。
为了方便大家对照文章查看代码,我们复制上一篇《服务间的调用》的代码,开一个新的多模块项目,然后在这个项目上进行扩展,增加权限控制。
先上一个图,看看增加权限控制后,我们的微服务系统是什么样的:
从图中可以看出,需要增加一个认证中心,另外需要进行权限控制的微服务也需要进行一些调整,跟认证中心协作进行资源的访问控制。认证中心实现用户的认证和授权,比如验证用户的账号和密码(认证),然后把这个用户的账号和角色封装为JWT令牌进行签发(授权)。服务A和服务B接收到用户请求后,根据携带的JWT令牌实际执行资源的访问控制(鉴权)。
接下来我们就按这个思路来进行认证中心和资源服务器的开发。
版权声明: 本文为 InfoQ 作者【xiaoboey】的原创文章。
原文链接:【http://xie.infoq.cn/article/a46ecb3c5da25b7aea79c58ae】。文章转载请联系作者。
评论