神即道 道法自然 如来

认证鉴权与API权限控制在微服务架构中的设计与实现(二)

引言: 本文系《认证鉴权与API权限控制在微服务架构中的设计与实现》系列的第二篇,本文重点讲解用户身份的认证与token发放的具体实现。本文篇幅较长,对涉及到的大部分代码进行了分析,可收藏于闲暇时间阅读,欢迎订阅本系列文章。

1. 系统概览

在上一篇【 认证鉴权与API权限控制在微服务架构中的设计与实现(一)】介绍了该项目的背景以及技术调研与最后选型,并且对于最终实现的endpoint执行结果进行展示。对系统架构虽然有提到,但是并未列出详细流程图。在笔者的应用场景中,Auth系统与网关进行结合。在网关出配置相应的端点信息,如登录系统申请token授权,校验check_token等端点。

下图为网关与Auth系统结合的流程图,网关系统的具体实现细节在后面另写文章介绍。(此处流程图的绘制中,笔者使用极简的语言描述,各位同学轻喷😆!)

上图展示了系统登录的简单流程,其中的细节有省略,用户信息的合法性校验实际是调用用户系统。大体流程是这样,客户端请求到达网关之后,根据网关识别的请求登录端点,转发到Auth系统,将用户的信息进行校验。

另一方面是对于一般请求的校验。一些不需要权限的公开接口,在网关处配置好,请求到达网关后,匹配了路径将会直接放行。如果需要对该请求进行校验,会将该请求的相关验证信息截取,以及API权限校验所需的上下文信息(笔者项目对于一些操作进行权限前置验证,下一篇章会讲到),调用Auth系统,校验成功后进行路由转发。

这篇文章就重点讲解我们在第一篇文章中提到的用户身份的认证与token发放。这个也主要包含两个方面:

  • 用户合法性的认证
  • 获取到授权的token

2. 配置与类图

2.1 AuthorizationServer主要配置

关于AuthorizationServerResourceServer的配置在上一篇文章已经列出。AuthorizationServer主要是继承了AuthorizationServerConfigurerAdapter,覆写了其实现接口的三个方法:

 

2.2 主要Authentication类的类图

主要的验证方法authenticate(Authentication authentication)在接口AuthenticationManager中,其实现类有ProviderManager,有上图可以看出ProviderManager又依赖于AuthenticationProvider接口,其定义了一个List<AuthenticationProvider>全局变量。笔者这边实现了该接口的实现类CustomAuthenticationProvider。自定义一个provider,并在GlobalAuthenticationConfigurerAdapter中配置好改自定义的校验provider,覆写configure()方法。

AuthenticationManagerBuilder是用来创建AuthenticationManager,允许自定义提供多种方式的AuthenticationProvider,比如LDAP、基于JDBC等等。

3. 认证与授权token

下面讲解认证与授权token主要的类与接口。

3.1 内置端点TokenEndpoint

Spring-Security-Oauth2的提供的jar包中内置了与token相关的基础端点。本文认证与授权token与/oauth/token有关,其处理的接口类为TokenEndpoint。下面我们来看一下对于认证与授权token流程的具体处理过程。

上面给代码进行了注释,读者感兴趣可以看看。接口处理的主要流程就是对authentication信息进行检查是否合法,不合法直接抛出异常,然后对请求的GrantType进行处理,根据GrantType,进行password模式的身份验证和token的发放。下面我们来看下TokenGranter的类图。

可以看出TokenGranter的实现类CompositeTokenGranter中有一个List<TokenGranter>,对应五种GrantType的实际授权实现。这边涉及到的getTokenGranter(),代码也列下:

 

本次请求是使用的password模式,随后进入其GrantType具体的处理流程,下面是grant()方法。


上面一段代码是grant()方法具体的实现细节。GrantType匹配到其对应的grant()后,先进行基本的验证确保安全,然后进入主流程,就是下面小节要讲的验证身份和发放token。

3.2 自定义的验证类CustomAuthenticationProvider

CustomAuthenticationProvider中定义了验证方法的具体实现。其具体实现如下所示。

authenticate()最后返回构造的自定义CustomAuthenticationToken,在CustomAuthenticationToken中,将boolean authenticated设为true,user信息验证成功。这边传入的参数CustomUserDetails与token生成有关,作为payload中的信息,下面会讲到。

AbstractAuthenticationToken实现了接口Authentication和CredentialsContainer,里面的具体信息读者可以自己看下源码。

3.3 关于JWT

用户信息校验完成之后,下一步则是要对该用户进行授权。在讲具体的授权之前,先补充下关于JWT Token的相关知识点。

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

从上面的描述可知JWT的定义,这边读者可以对比下token的认证和传统的session认证的区别。推荐一篇文章什么是 JWT – JSON WEB TOKEN,笔者这边就不详细扩展讲了,只是简单介绍下其构成。

JWT包含三部分:header头部、payload信息、signature签名。下面以上一篇生成好的access_token为例介绍。

  • header

jwt的头部承载两部分信息,一是声明类型,这里是jwt;二是声明加密的算法 通常直接使用 HMAC SHA256。第一部分一般固定为:

  • playload

存放的有效信息,这些有效信息包含三个部分、标准中注册的声明、公共的声明、私有的声明。这边笔者额外添加的信息为X-KEETS-UserIdX-KEETS-ClientId。读者可根据实际项目需要进行定制。最后playload经过base64编码后的结果为:

  • signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:header (base64后的)、payload (base64后的)、secret。
关于secret,细心的读者可能会发现之前的配置里面有具体设置。前两部分连接组成的字符串,通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。第三部分结果为:


3.3 自定义的AuthorizationTokenServices

现在到了为用户创建token,这边主要与自定义的接口AuthorizationServerTokenServices有关。AuthorizationServerTokenServices主要有如下三个方法:


由于篇幅限制,笔者这边仅对createAccessToken()的实现方法进行分析,其他的方法实现,读者可以下关注笔者的GitHub项目。


这边具体的实现在上面有注释,基本没有改写多少,读者此处可以参阅源码。createAccessToken()还调用了两个私有方法,分别创建accessToken和refreshToken。创建accessToken,需要基于refreshToken。

此处可以自定义设置token的时效长度,accessToken创建实现如下:


既然提到TokenEnhancer,这边简单贴一下代码。


自此,用户身份校验与发放授权token结束。最终成功返回的结果为:

4. 总结

本文开头给出了Auth系统概述,画出了简要的登录和校验的流程图,方便读者能对系统的实现有个大概的了解。然后主要讲解了用户身份的认证与token发放的具体实现。对于其中主要的类和接口进行了分析与讲解。下一篇文章主要讲解token的鉴定和API级别的上下文权限校验。

本文的源码地址:
GitHub:https://github.com/keets2012/Auth-service
码云: https://gitee.com/keets/Auth-Service


参考

什么是 JWT – JSON WEB TOKEN

Re:从零开始的Spring Security OAuth2(二)

spring-security-oauth

来源:http://blueskykong.com/2017/10/22/security2

点赞