童 敏,张黎娜,梁伍七
(安徽开放大学 信息与工程学院,安徽 合肥 230022)
微服务作为一种新的软件架构模式,将系统业务功能进行细粒度的划分。其功能模块被称为服务,每个服务可独立部署并运行在其独立的进程中,服务间则采用轻量级的通信机制进行沟通[1-2]。和传统的面向服务的架构(Service Orienented Architechture,SOA)相比,微服务具有更高的灵活性、可实施性和可扩展性,是一种可独立测试、部署和运行的软件架构模式,在SOA的基础上融入了组件化思想和领域建模思想。微服务架构将软件整体功能分解成多个服务,分别由不同类别的服务器进行支持[3]。可以直观地将微服务理解为细粒度的SOA,其具有服务组件化、按业务组织团队、轻量级通信、去中心化治理以及去中心化数据治理等特性[4]。相比传统分布式服务来说,微服务的粒度更小,服务之间耦合度更低,服务的敏捷性更高,是分布式服务的发展趋势。
传统的Web应用通常采用Cookie-Session模式进行用户身份认证,在服务端生成用户信息数据保存在Session中,并将在服务端产生的Cookie信息以响应报文的方式返回给客户端。用户再次发送请求时附带服务器返回的Cookie,服务器根据存储的Session信息和附带的Cookie对用户的身份进行认证[5]。分布式应用系统中每个服务都会有认证和授权的需求,采用传统的用户身份认证将面临Session信息共享的问题。
在基于微服务架构的分布式应用系统中,用户的请求可能会由不同的服务器来处理,这就面临着单个应用服务器中的Session信息如何共享的问题。Session共享的解决方案通常有:所有服务器通过同步进行Session复制;用户的每次请求强制分发到特定的服务器进行Session绑定;在单独的服务器上使用Redis缓存技术实现Session集中管理;基于Cookie管理进行Session共享等。基于Session的认证方案在服务端对会话进行控制,具有较高的安全性。但Session方案需要基于Cookie进行认证,在移动客户端上不能被有效使用;而分布式应用系统中,Session的复制、绑定以及集中管理等共享解决方案都会增加系统的网络开销[6],见图1。
图1 分布式系统基于Session的认证方式
为了克服分布式系统中基于Session认证的不足,可以采用基于Token的分布式系统认证方案,见图2。用户通过用户名和密码向身份认证服务器发送登录认证请求,服务端认证成功后会生成一个带签名的Token发给客户端,客户端可以放到Cookie或LocalStorage等存储对象中;发送资源授权服务请求时,在请求头中携带Token信息,身份认证服务对Token信息进行解析校验,通过用户身份认证后返回用户角色的权限信息,若用户具有访问该资源的权限,则资源授权服务将请求的资源数据返回给用户,否则返回用户权限不足的信息。
图2 基于Token认证的授权过程
和传统的认证方式相比较,基于Token认证的优点是,支持跨域访问,无状态服务端可扩展,能够防止跨站域请求伪造(CSRF),优化网络开销和系统性能等。但基于Token认证也有自身的不足,Token由于自包含信息,数据量较大,而且每次请求都要传递Token,比较占用带宽。另外,服务端签发Token时包含了签发时间和失效时间,因此存在超过有效时间需要重新登录客户端的问题[7]。基于Token的认证方式下,服务端不用存储用户信息,不需要考虑用户在哪台服务器登录过,解决了在服务端存储用户信息的诸多问题。因此,Token认证方式更适合前后端分离的分布式应用系统。
JWT(Json Web Token)是一种基于JSON的开发标准(RFC 7519),由三部分组成,分别是头部信息(Header)、载荷信息(Payload)和签名信息(Signature),每部分中间使用点(.)分隔[8]。其中头部包括Token的类型及使用的哈希算法;载荷部分可以存放签发者、过期时间戳以及面向的用户等内置字段,也可自定义字段;签名部分用于防止JWT内容被篡改,使用Base64Url将前面两部分进行编码,编码后使用点(.)连接组成字符串,最后使用Header中声明的签名算法进行签名。特别适用于分布式应用的单点登陆(SSO)场景。在目前的Token认证中,越来越多地使用了JWT认证。它基于JSON格式数据并采用加密算法进行签名,传递的信息通过数字签名可以被验证和信任。JWT可以使用HMAC算法或者RSA的公钥/私钥核对签名,防止信息被篡改。
基于JWT的登录认证过程如下:(1)首次登录,客户端输入用户名和密码,Post提交后交由服务器的LoginAction处理;(2)LoginAction调用认证服务进行认证,若认证通过,LoginAction调用用户信息服务得到用户完整信息;(3)LoginAction得到用户完整信息后,从配置文件获取签名秘钥,开始Token的生成;(4)Token生成过程中可以调用第三方的JWT生成库生成JWT签名数据;(5)LoginAction将JWT签名数据设置到Cookie中,或者设置到Http请求的HeaderAuthorization字段中,并重定向到首页,完成登录认证。
基于Token的认证机制会在每一次请求中都带上完成签名的Token信息,基于JWT的请求认证过程如下:(1)客户端通过Post或Get请求系统资源访问;(2)认证服务拦截请求,并在Cookie中或者AuthorizationHead查找Token信息;(3)如果找到Token信息,则根据配置文件中的签名秘钥,调用JWT库对Token信息进行解密;(4)完成解码并验证签名通过后,对Token中的过期时间(exp)、该时间之前不处理(nbf)、用户(aud)等信息进行验证;(5)验证通过后,获取用户角色权限信息,对请求资源的访问权限进行判断;(6)若具有访问该资源的权限,则资源授权服务将资源数据返回给用户,否则返回用户权限不足的信息。
Spring Security是Spring生态系统中的一员,是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。OAuth 2.0协议规定第三方获取资源必须按照获取认证、获取访问资源令牌以及通过令牌获取指定资源的顺序进行,Spring Security组件集成了对该协议的支持[9]。Spring Security用以解决企业应用的安全访问控制,其实现机制是通过过滤器链对系统的请求进行拦截,校验请求是否可以访问对应的系统资源[10],其过滤器链如图3所示。
图3 Spring Security过滤器链
SecurityContextPersistenceFilter是整个拦截过程的入口和出口;UsernamePasswordAuthenticationFilter用于处理来自表单提交的认证,表单必须提供对应的用户名和密码;FilterSecurityInterceptor用于保护系统资源;ExceptionTranslationFilter负责捕获来自过滤器链的所有异常,处理AuthenticationException和AccessDeniedException异常,并将其他异常继续抛出。
在使用用户名和密码的认证场景中,Spring Security安全框架认证管理器(AuthenticationManager)认证过程如下:(1)用户提交的用户名、密码被过滤器链中的UsernamePasswordAuthenticationFilter过滤器获取到,并将请求信息封装为Authentication(通常情况下以Authentication接口的UsernamePasswordAuthenticationToken这个实现类来处理)。(2)过滤器将Authentication提交至认证管理器AuthenticationManager进行认证。(AuthenticationManager接口是认证的核心接口,它的实现类为ProviderManager。Spring Security支持多种认证方式,ProviderManager维护着一个List
授权是用户认证通过以后,访问系统资源,Spring Security根据用户的权限进行校验的过程。Spring Security安全框架授权管理器(AccessDecisionManager)授权过程如下:(1)拦截请求,已认证用户访问受保护的web资源将被SecurityFilterChain中的FilterSecurityInterceptor的子类拦截。(2)获取资源访问策略,FilterSecurityInterceptor通过SecurityMetadataSource.getAttributes()获取访问当前资源所需要的权限,返回权限集合Collection
授权的方式包括Web授权和方法授权,Web授权通过url拦截进行授权,方法授权通过方法拦截进行授权。两种方式都调用AccessDecisionManager.Decide()进行授权决策,若为Web授权则拦截器为FilterSecurityInterceptor,若为方法授权则拦截器为MethodSecurityInterceptor[11]。如果同时通过Web授权和方法授权则先执行Web授权,再执行方法授权,最后决策通过,则允许访问资源,否则将禁止访问。
基于微服务架构的分布式应用系统中,可以将认证授权服务交由一个单独的微服务来实现,以完成整个系统认证授权的请求。根据上述分布式系统认证方案的分析,采用基于Token的分布式系统认证方式,使用JWT格式来存储Token,分布式系统认证技术方案如图4所示。
图4 分布式系统认证技术方案
认证微服务UAA组件提供用户登录和接入认证、授权以及令牌生成的功能;API网关作为系统的入口,主要功能是统一接入、协议适配、流量管理以及安全防护等功能,还具有身份认证、负载均衡、限流降级以及系统缓存等功能[12]。接入方通过统一的API网关接入微服务,系统的非业务功能交由API网关处理,资源微服务则提供系统的业务功能。
研究案例实验采用基于Spring Security的安全框架和基于Spring Cloud的微服务平台框架,并采用JWT格式存储Token信息进行认证,使用Maven技术对项目进行管理和构建;系统由父工程distributed-security以及UAA、resource、gateway和discovery四个模块组成,UAA模块作为认证微服务,resource模块作为资源微服务,gateway模块作为API网关微服务,采用Spring Cloud Gateway网关组件,discovery模块作为注册中心微服务,注册中心采用Nacos来发现、配置和管理微服务。
登录认证和权限验证主要由两个过滤器类JWTLoginFilter和JWTAuthenticationFilter实现。JWTLoginFilter类继承自UsernamePasswordAuthenticationFilter,功能是验证用户名密码;验证通过后会生成一个Token,并将其返回给客户端。其采用重写attemptAuthentication和successfulAuthentication的方法,使用attemptAuthentication接收并解析用户凭证,用户成功登录后调用successfulAuthentication,在该方法中生成Token,核心代码如图5。
图5 JWTLoginFilter类过滤器核心代码
JWTAuthenticationFilter类继承自BasicAuthenticationFilter,功能是实现Token的校验。在doFilterInternal方法中,首先从http头的Authorization项读取Token数据,然后用Jwts包提供的方法校验token的合法性,若校验通过,则请求合法,其核心代码如图6。
图6 JWTAuthenticationFilter类过滤器核心代码
在基于微服务架构的分布式应用系统中,传统的基于Session模式的用户身份认证存在Session共享的问题,多种解决方案都会增加系统的网络开销;而基于Token的分布式系统认证方案可以很好地解决这些问题。在分布式系统中,认证授权功能可以由一个单独的微服务实现,系统中的认证和授权的需求都可以交由这个单独的微服务完成。在Token认证方式中,采用JWT格式来存储Token信息。采用Spring Cloud微服务框架和Spring Security安全框架,设计并实现了基于JWT的认证授权原型系统。基于JWT的Token认证授权机制携带的信息签名能保证数据不被篡改,为分布式应用系统提供安全可靠的认证授权保障,在基于微服务架构的分布式应用系统应用中具有良好的应用场景。