一、前言
我们知道微服务技术或微服务架构是一把双刃剑,其给我们带来了简单、灵活的部署,聚焦、专注快速迭代,低耦合、高内聚、易扩展等优势;与此同时,也引入了更加复杂的问题。本文要着重阐述的如何在微服务架构中实现一个安全、高效的认证和鉴权方案。
二、单体应用认证和鉴权
我们先回想下,在单体应用架构时我们是怎么对用户进行认证和鉴别权限的?在单体架构下,往往会采用一个安全模块来实现用户认证和鉴权。
用户认证
当用户登录时,应用内的安全模块对用户身份进行验证,验证用户身份合法后,为该用户生成一个会话(Session),并为该Session关联一个唯一的编号(Session Id)。Session是应用中的一小块内存结构,其中保存了登录用户的信息,如User name, Role, Permission等。应用服务会将该Session的Session Id返回给客户端,客户端将Session Id以cookie或者URL重写的方式记录下来,并在后续请求中发送给应用,这样应用在接收到客户端访问请求时可以使用Session Id验证用户身份,不用每次请求时都输入用户名和密码进行身份验证。用户鉴权
在完成了用户认证后,应用服务对用户的每一个请求携带过来的Session Id,进行对用户权限判断用户,以此判断用户是否能执行该此请求,以实现操作鉴权。
三、微服务认证和鉴权
3.1 面临的问题
在微服务架构中,我们知道微服务的粒度切分以业务边界为依据的。一个庞大的系统中,根据业务会划分出很多的微服务。对每一个微服务的访问请求都需要进行认证和鉴权,微服务的认证和鉴权又面临哪些问题?
微服务架构下的认证和鉴权涉及到场景更为复杂,涉及到用户访问微服务应用,第三方应用访问微服务应用,微服务之间相互访问等多种场景,每种场景下的认证和鉴权方案都需要考虑到,如何来保证复杂场景下微服务的安全性?
认证和鉴权逻辑,在微服务架构中放在什么位置、哪一层,更为合适?
认证和鉴权成功后的信息如何存储,如何维护调用方和服务方之间的认证关系?
微服务应用的权限粒度,如何做到API级别的控制?
3.2 技术方案
3.2.1 用户身份认证
在微服务体系架构中,微服务应用是由多个相互独立的微服务进程组成的,对每个微服务的访问都需要进行用户认证。
微服务应用应遵循单一职责原理,即一个微服务只处理单一的业务逻辑。认证和鉴权的公共逻辑不应该放到微服务实现中。因此需要考虑一个抽象、公共的逻辑对用户进行身份认证和鉴权服务。由于在微服务架构中以API Gateway作为对外提供服务的入口,因此可以考虑在API Gateway处提供统一的用户认证。具体就技术实现采用Zuul+Spring Security+OAuth2/JWT。
3.2.2 用户状态保持
在单体应用时,我们是在服务器端采用Session和客户端采用Cookie来保存用户状态,由于在服务器是有状态的,对服务器的水平扩展有影响。
我们知道微服务架构的优势之一是微服务的水平扩展(Scalability)和弹性(Resiliency),所以微服务最好是无状态的。因此建议采用Token来记录用户登录状态。
Token和Seesion主要的不同点是存储的地方不同。
Session是集中存储在服务器中的;而Token是用户自己持有的,一般以Cookie的形式存储在浏览器中。Token中保存了用户的身份信息,每次请求都会发送给Gateway,因此可以判断访问者的身份,并判断其对请求的资源有没有访问权限。
Token用于表明用户身份,其又以Cookie的形式存储在浏览器中。为了保障信息的安全,因此需要对其内容进行加密,避免被请求方或者第三者篡改。
JWT(Json Web Token)是一个定义Token格式的开放标准(RFC 7519),定义了Token的内容、加密方式并提供了各种语言的lib。
JWT Token的结构非常简单,包括三部分:
Header
头部包含类型,为固定值JWT。然后是JWT使用的Hash算法。
{
"alg": "HS256",
"typ": "JWT"
}
Payload
包含发布者,过期时间,用户名等标准信息,也可以添加用户角色,用户自定义的信息。
{ "sub": "1QAZWSX2", "name": "WY", "admin": true }
Signature
Token颁发方的签名,用于客户端验证Token颁发方的身份,也用于服务器防止Token被篡改。
签名算法HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
,这三部分使用Base64编码后组合在一起,成为最终返回给客户端的Token,每部分之间采用”.“分隔。
采用Token进行用户认证,服务器端不再保存用户状态,客户端每次请求时都需要将Token发送到服务器端进行身份验证。
Token发送的方式rfc6750进行了规定,采用一个Authorization: Bearer HHTP Header进行发送。
Authorization: Bearer 37mF-90B5f-86JqM
采用Token方式进行用户认证的基本流程如下图所示:
用户输入用户名,密码等验证信息,向服务器发起登录请求
服务器端验证用户登录信息,生成JWT token
服务器端将Token返回给客户端,客户端保存在本地(一般以Cookie的方式保存)
客户端向服务器端发送访问请求,请求中携带之前颁发的Token
服务器端验证Token,确认用户的身份和对资源的访问权限,并进行相应的处理(拒绝或者允许访问)
关于Token注销
由于Token不存储在服务端,由客户端存储。当用户注销时,Token的有效时间还没有到,还是有效的。所以如何在用户注销登录时让Token注销是一个要关注的点。一般有如下几种方式:
Token存储在Cookie中,这样客户端注销时,自然可以清空掉
将Token存放到分布式缓存中,每次校验Token时区检查下该Token是否已注销。不过这样也就失去了快速校验Token的优点。
多采用短期令牌,比如令牌有效期是30分钟,这样可以一定程度上降低注销后 Token可用性的风险。
3.2.3 单点登录
我们看下维基百科中,对单点登录的解释。
单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。相同的,单一注销(single sign-off)就是指,只需要单一的注销动作,就可以结束对于多个系统的访问权限。
在微服务架构中,单点登录可以理解为当用户登录成功后,就可以访问所有有权限访问的微服务。API Gateway提供了客户端访问微服务的入口,Token实现了无状态的用户认证。结合这两种技术,我们就可以为微服务应用实现一个单点登录方案。
微服务单点登录流程下,用户的认证和鉴权如下图:
用户登录
客户端发送登录请求到API Gateway
API Gateway将登录请求转发到Security Service
Security Service验证用户身份,并颁发Token
用户请求
客户端请求发送到API Gateway
API Gateway调用的Security Service对请求中的Token进行验证,检查用户的身份
如果请求中没有Token,Token过期或者Token验证非法,则拒绝用户请求。
Security Service检查用户是否具有该操作权
如果用户具有该操作权限,则把请求发送到后端的Business Service,否则拒绝用户请求。
3.2.4 用户权限控制
API Gateway处进行统一的权限控制
客户端发送的HTTP请求中包含有请求的Resource及HTTP Method。如果系统遵循REST规范,以URI资源方式对访问对象进行建模,则API Gateway可以从请求中直接截取到访问的资源及需要进行的操作,然后调用Security Service进行权限判断,根据判断结果决定用户是否有权限对该资源进行操作,并转发到后端的Business Service。这种实现方式API Gateway处统一处理认证和鉴权逻辑,各个微服务不需要考虑用户认证和鉴权,只需要处理业务逻辑,简化了各微服务的实现。
3.2.5 微服务之间的认证
微服务应用,除了来自用户和第三方的访问外,还有大量的微服务之间访问。根据微服务应用的数据敏感程度的不同,对于微服务之间的相互访问可能有不同的安全要求
。
微服务间的相互访问不进行认证和鉴权
依托网络安全措施,来保障微服务间的通讯安全
微服务提供的数据敏感程度不是很高
在这种情况下,一旦攻击者侵入到内部网络后没有了保护措施。虽然微服务的数据敏感程度不高,但是攻击行为仍然给我们带来危害。
服务间的访问采用颁发访问凭证进行安全控制
微服务在使用的纬度上,我们定义服务的调用方和服务的提供方。服务的提供方对调用方颁发访问凭证,提供方对访问严格控制;没有访问凭证的访问,拒绝访问;根据不同的访问凭证类型,安全控制不同的访问类型。服务间的访问采用Service Account进行安全控制
Istio-Auth提供强大的服务间认证和终端用户认证,使用交互TLS,内置身份和证书管理。可以升级服务网格中的未加密流量,并为运维人员提供基于服务身份而不是网络控制来执行策略的能力。Istio的未来版本将增加细粒度的访问控制和审计,以使用各种访问控制机制(包括基于属性和角色的访问控制以及授权钩子)来控制和监视访问您的服务,API或资源的人员。
Istio Security Architecture
Istio-Auth更多信息:https://istio.io/docs/concepts/security/mutual-tls/
3.2.6 第三方应用
OAuth2
OAuth2针对不同场景有不同的认证流程,一个典型的认证流程如下图所示:
(图片来源于网络)
用户向OAuth客户端程序发起一个请求,OAuth客户端程序在处理该请求时发现需要访问用户在资源服务器中的数据。
客户端程序将用户请求重定向到认证服务器,该请求中包含一个callback的URL。
认证服务器返回授权页面,要求用户对OAuth客户端的资源请求进行授权。
用户对该操作进行授权后,认证服务器将请求重定向到客户端程序的callback url,将授权码返回给客户端程序。
客户端程序将授权码发送给认证服务器,请求token。
认证服务器验证授权码后将token颁发给客户端程序。
客户端程序采用颁发的token访问资源,完成用户请求。
POST /oauth/token HTTP/1.1
Host: authorization-server.comgrant_type=authorization_code
&code=xxxxxxxxxxx
&redirect_uri=https://example-app.com/redirect
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx
(引用:https://www.oauth.com/oauth2-servers/access-tokens/authorization-code-request/)
四、总结
微服务架构下,认证和鉴权涉及到场景复杂,涉及到用户访问微服务应用,第三方应用访问微服务应用,微服务之间相互访问等多种场景。
身份认证。确认“你是你”,获取你的身份信息。
权限验证。确认“你能做某件事”。
权限建模时,往往会拆分成功能权限和数据权限。上述的解决方案,从功能权限结合各种微服务应用场景下,抽象了身份认证和权限验证模型。
-EOF-
热门评论
大佬讲的非常清晰,言简意赅!