微服务认证授权:常见的授权方案
1.前言
前面我们讨论的是SpringSecurity基础部分内容,接下来我们来探讨一下SpringCloud 集成 SpringSecurity和Oauth实现微服务认证授权方案
2.微服务(分布式)项目常见认证方案
2.1.微服务授权面临哪些问题
在微服务架构下有很多的服务,每个微应用都需要对访问进行认证检查和权限控制,客户端发起一个请求需要考虑如何让用户的认证状态通知到所有的微服务中,尤其是请求来源于多种客户端如浏览器,移动端,三方程序,服务之间访问时,微服务的授权变得更加麻烦,再加上本地Session在微服务(集群/分布式)环境中存在Session不同步的问题,所以我们微服务授权是非常非常复杂的。
Session不同步问题如图:
2.2.微服务常见认证方案
2.2.1.CAS单点登录
CAS是一种基于Cookie实现的单点登录方案,页是一个比较老的解决方案,是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,它分为 CAS Server端和CAS Client端,Server端负责用户的登录流程,Client端需要整合到各个系统中,它的登录流程如下:
系统A的访问流程:
- 浏览器请求系统A,系统A检查到没有登录,重定向到认证服务,并且携带参数service,这个参数的作用是在认证中心认证通过之后会再跳回系统A的重定向地址
- 请求跳转到认证中心,认证中心接收到请求,返回登录页面,用户提交登录信息
- 认证中心执行登录逻辑,认证成功,生成ticket(ST)和 TGC,认证中心会将TGC写到浏览器的cookie,有这个东西说明用户登录过,通过TGC可以找到TGT
- 然后认证中心将请求再重定向到系统A(根据之间的service参数回调地址),并且携带一个ticket参数
- 请求跳转回了系统A,系统A得到ticket,向认证中心发起请求校验ticket的合法性
- 认证中心验证ticket通过,向client返回对应的用户信息
系统B的访问流程:
- 浏览器(同一个浏览器)访问另外一个客户端系统B,系统B检查到没有登录,请求重定向到认证中心
- 由于浏览器在访问系统A的时候已经到了登录,cookie中有TGC,所以认证中心可以获取到TGC找到对应的TGT
- 认证中心根据cookie中的TGC找到TGT,代表用户登录过,于是创建ticket,并重定向请求到系统B,带着参数ticket
- 系统B接收到请求,拿着ticket去认证中心校验Ticket
- 认证中心ticket校验通过,返回用户信息
这种方案意味着每个面向用户的服务都必须与认证服务交互,这会产生大量非常琐碎的网络流量和重复的工作,当动辄数十个微应用时,这种方案的弊端会更加明显,而且这种方案不太适合移动端认证方案。
2.2.2.分布式Session(会话)+网关
这种方案是在网关做登录,以及登录检查,登录会话通过Redis进行分布式会话存储,后端微服务可以从共享会话Redis中获取认证信息,当然这里我们可以使用Redis的 key 来生成一个Token然后相应给客户端,客户端存储Token。当访问资源的时候携带者Token去后台,在网关层根据Token检查分布式会话是否完成登录。
- 客户端发起请求,网关实现登录逻辑,登录信息存储到Redis,并且根据Redis的key创建一个Token
将Token返回给浏览器,浏览器将Token存储起来 - 浏览器在访问资源的时候请求到网关并携带Token,网关获取到Token,从Redis中获取用户登录信息做检查,没有问题就继续转发Token到后续服务。
- 在后续服务中可以通过Token去Redis中获取认证信息。
这种方案需要注意对共享会有一定的保护机制,并且实现也有一定的实现难度
2.2.3.客户端Token+网关
客户端Token是一种比较常用的认证方案,有点在于Token中携带了用户信息在服务之间传输,做到了无状态,可以通过JWT等安全机制加密Token保证Token的安全性
- 客户端发起认证请求,使用JWT等加密方式生成安全的 Token,Token中携带了认证授权信息,然后返回给客户端,
- 客户端存储Token,后续客户端需要访问资源时,携带者Token请求。
- 客户端发起请求,在网关层对Token进行统一检查,检查通过Token继续携带到后端访问中如果涉及到大量的用户信息的存放,可以使用Redis来进行存储。
- 后续服务获取到Token即可获取到用户信息
客户端Token方案的好处在于可以做到无状态,因为Token中包含了用户信息,服务端不用考虑存储用户信息,缺点在于Token过长造成的网络传输的开销。
2.2.4.其他方案
当然解决单点登录还有其他方案,比如使用SpirngSession+Redis实现Session共享,或是配置服务器之间的Session同步,亦或是使用Nginx的负载均衡策略url hash一致性来解决,总归来说解决方案五花八门,选择适合项目的方案是最重要的
2.3.如何选择
- CAS单点登录方案是比较早期的方案,且会产生大量网络请求,消耗资源;【不推荐】
- 分布式Session(Redis):适用于中小型项目,采用Redis来代替Session,我们项目二就是采用的这个方案;
- 客户端Token:适用于大型项目(微服务架构项目);
2.3.1.SpringCloud+SpringSecurity+OAuth2+JWT
2.3.2.SpringCloud+SpringSecurity+OAuth2+JWT+Zuul
解释:
- 客户端 : web端,移动端,三方程序
- 认证服务:Oauth2授权服务:负责认证逻辑(登录)和颁发令牌(token)等
- 网关:Oauth2资源服务:负责token统一鉴权
- 资源服务:用户对资源的访问权限检查和返回资源
它的大概流程是:
- 客户端向认证服务发起认证请求,认证服务执行认证流程
- 认证成功,认证服务根据用户认证信息,授权信息颁发Token,Token中包含了认证授权信息
- 客户端存储Token,并向资源服务发起请求,请求头携带Token
- 请求先到达Zuul网关,我们可以在网关层校验Token,当然也可以不在网关层校验Token,而是在每个资源服务器上校验Token
- Token校验通过,资源服务获取到Token中的权限信息对资源进行授权,授权成功返回资源数据,授权失败返回错误
- 如果请求需要多个资源服务共同完成,那么我们还需要考虑在服务调用的使用把Token通过请求头传递给下一个被调用的微服务进行授权