一、预备知识

本文讨论基于微服务架构下的身份认证和用户授权的技术方案,在阅读之前,最好先熟悉并理解以下几个知识点:

  • 微服务架构相关概念:服务注册、服务发现、API 网关
  • 身份认证和用户授权:SSO、CAS、OAuth2、JWT

文章在涉及到上述知识内容时,会附上参考链接。

二、背景

当企业的应用系统逐渐增多后,每个系统单独管理各自的用户数据容易行成信息孤岛,分散的用户管理模式阻碍了企业应用向平台化演进。当企业的互联网业务发展到一定规模,构建统一的标准化账户管理体系将是必不可少的,因为它是企业互联网云平台的重要基础设施,能够为平台带来统一的帐号管理、身份认证、用户授权等基础能力,为企业带来诸如跨系统单点登录、第三方授权登录等基础能力,为构建开放平台和业务生态提供了必要条件。

三、需求分析

在微服务架构下,必须对企业的平台生态进行合理的业务划分,每个业务板块将自成系统,例如负责宣发的企业官网、主打文体的 B2B2C 商城、面向社区的物业服务系统等,这些系统业务比较独立,应当独立拆分。每个系统又可根据各自的业务模型进行切分,将业务模型和用户需求统筹分析后建立恰当的领域模型,形成独立的服务。
另外,企业平台的客户范围比较复杂,有 2B 的业务,也有 2C 的,还有 2G(goverment)的,因此平台级的统一身份管理必须涉及组织实体和个人实体两类,其中组织实体包括政府机关(G)、企业单位(B)、团体组织(B)等,这类似于多租户架构的概念,但又比传统多租户架构复杂。

一)统一身份管理(Unified Identity Manager)

统一身份管理(UIM)是整个平台帐号和权限管控的基础,由此构建的系统称为UIMS(Unified Identity Management System),平台下所有系统的账户管理、身份认证、用户授权、权限控制等行为都经由 UIMS 处理,提供帐号密码管理、基本资料管理、角色权限管理等功能。UIMS 基于『统一身份治理』的概念,可划分为两级账户体系、基础权限模块和基础信息模块三大模块。其中两级账户体系将账户分为组织实体帐号和个人实体账户两大类,个人实体从属于组织实体,也可以不从属任何组织实体,且个人实体可同时从属于多个组织实体;基础权限模块将各业务系统的资源权限进行统一管理和授权;基础信息模块用于描述组织实体和个人实体的基本信息,如组织实体名称、地址、法人,个人实体姓名、电话号码、性别等基础信息。UIMS 提供统一的 API 与各子系统连接。
可以看到,众多系统和众多服务都将依赖于 UIMS 。本文仅涉及 UIMS 下的身份认证和用户授权这两块,即两级账户体系和基础权限模块,据此可以独立出账户服务和鉴权服务,也可以合并成一个账户鉴权服务。

关于统一身份管理系统的介绍,请参考 https://mtide.net/平台级SAAS架构的基础-统一身份管理系统.html

二)软件即服务(SAAS)

企业提供对外的 IT 服务,有两种部署模式:一是私有云部署,二是公有化服务。公有云服务即 SAAS,提供系统级的应用服务,包括企业服务如企业邮箱、办公 OA、人力资源系统等,个人服务如个人邮箱、云笔记、云网盘等。平台级的 SAAS 应用架构,实际上是一种多租户架构的升级版,加大了统一身份治理的难度。而基于 UIMS 系统的两级账户体系,可以很容易做到这一点。值得注意的是,有些系统仅提供个人账户服务,有些系统仅提供组织账户服务,有的则两者都提供,必须处理好个人实体和组织实体之间的关系。

关于 SAAS 的介绍,请参考 http://www.ruanyifeng.com/blog/2017/07/iaas-paas-saas.html

三)组织实体(Orginization Entity)

在 UIMS 中,组织机构应当是一种实体,与之对应的另一种实体是个人实体。注意实体(Entity)不是账户(Account),因此要设计一种用于组织实体登入受控系统的方法,这里有两种可选方案:一是增加组织实体账户,组织实体自身拥有账户,可直接进行认证登录;二是将从属于组织实体的个人账户作为组织实体的登入凭证。无论何种方法,在认证和授权时,都应当向 UIMS 提供统一的标准的账户凭证,凭证的规格由 UIMS 定义,因此,组织实体的认证授权与个人实体的认证授权并无二致。

四)单点登录(SSO)

企业平台涉及众多子系统,为简化各子系统的用户管理,提升用户体验,因此实现 SSO 是统一身份认证的重要目标:一次登录,全部访问。对于企业内部应用来说,SSO 是必须的选项,例如企业 OA、HR、CRM 等内部系统;对于外部应用来说,SSO 是可选项,具体哪个应用应当加入 SSO 系统,由该业务系统决定,例如外部商城、物业系统等对外服务系统。无论何种应用是否采用 SSO,UIMS 在技术上应当具备 SSO 的能力。

关于SSO的介绍,请参考 https://www.cnblogs.com/EzrealLiu/p/5559255.html

五)授权登录

随着平台业务的逐渐增长,依托于平台的,和平台依托的厂商和客户等资源将极大的丰富平台,因此必须构筑开放的生态系统,以支撑业务的进一步发展。必须开放平台级的授权登录功能,以允许第三方应用接入。通过三方授权登录,将平台的服务各能力开发给第三方,并将第三方的服务和能力接入平台,繁荣共生,共同发展。

六)服务间鉴权

业务系统切分出不同的服务,根据粒度粗细和业务需求不同,服务的数量和权限要求都不同。微服务架构下的身份认证和授权,可分为两种:

  • 内部服务的认证和授权;
  • 外部服务的认证和授权。

通常,内部服务处于安全的内网环境之下,例如鉴权服务、商品服务、订单服务等,在对安全需求不高的情况下,可不执行认证过程,服务与服务之间是相互信任的。
而外部服务的认证和授权,通常由外部应用发起,通过反向代理或网关向安全边界内的服务发起请求,因此必须执行严格的认证过程。无线端APP、Web端、桌面客户端等外部应用下的各类服务,都属于外部服务。
微服务架构:统一身份认证和授权 - 图1

七)帐号登出和销毁

与 SSO 相对应,UIMS 应该支持一次登出,全部登出,即 SSOff(Single Sign-Off,非标准术语);或者一次登出,部分登出,而是否全部登出或部分登出取决于用户的选择,例如用户在 Web 端登出后,是否无线端 APP 也登出,这取决于用户偏好,但系统应当提供这种能力。
此外,必须提供统一的销毁功能,以支持用户删除其账户,一次销毁,全部销毁。

八)付费授权

云平台应具备付费授权机制,针对用户账户和组织账户进行独立授权。根据产品的商业策略,可执行灵活的付费模式:

  • 时效限制:年付、季付、月付,不同时效费用不同;
  • 功能限制:授权不同的功能,费用不同;
  • 数量限制:最大组织数量限制、最大用户数量限制,不同的数量费用不同。

    四、技术方案

    一)备选方案

    上文基于『统一身份治理』的理念,提出了统一身份管理系统(UIMS)下关于身份认证和授权部分的主要需求。目前实现统一身份认证和授权的技术手段较多,总体可以归纳为以下两类:
  1. 传统的 Cookie + Session 解决方案,有状态会话模式;
  2. 基于令牌/票据的解决方案,无状态交互模式。

具体有:

  • 分布式 Session
  • OAuth2.0
  • JWT
  • CAS

上述方案各有利弊:

  • 分布式 Session 是老牌的成熟解决方案,但因其状态化通信的特性与微服务提倡的API导向无状态通信相互违背,且共享式存储存在安全隐患,因此微服务一般不太采用。
  • OAuth2.0 是业内成熟的授权登录解决方案,然而 OA2.0 提供了4种授权模式,能够适应多种场景,作为基于令牌的安全系统,可以广泛用于需要统一身份认证和授权的场景。

    关于 OAuth2.0 的介绍,请参考 http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

  • JWT(JSON Web Token)是一种简洁的自包含的 JSON 声明规范,因其分散存储的特点而归属于客户端授权模式,广泛用于短期授权和单点登录。由于 JWT 信息是经过签名的,可以确保发送方的真实性,确保信息未经篡改和伪造。但由于其自包含的客户端验签特性,令牌一经签发,即无法撤销,因此单纯采用 JWT 作为统一身份认证和授权方案无法满足帐号统一登出和销毁、帐号封禁和解除这几种类型的需求。

    关于 JWT 的介绍,请参考 http://blog.leapoahead.com/2015/09/06/understanding-jwt/

  • CAS 是时下最成熟的开源单点登录方案,包含 CAS Server 和 CAS Client 两部分。CAS Server 是一个 war 包需要独立部署,负责用户认证;CAS Client 负责处理对客户端受保护资源的访问请求,需要认证时,重定向到 CAS Server。值得注意的是,CAS 是一个认证框架,其本身定义了一套灵活完整的认证流程,但其兼容主流的认证和授权协议如 OAuth2、SAML、OpenID 等,因此一般采用 CAS + OAuth2 的方案实现 SSO 和授权登录。

    关于 CAS 的介绍,请参考 https://apereo.github.io/cas/

在微服务架构下,身份认证和用户授权通常分离出来成为独立的鉴权服务。在做技术选型时,应从以下几点考虑:

  1. 满足 SSO 的技术需求;
  2. 满足简便性和安全性的需求;
  3. 满足开放性和扩展性的需求。

    综合考虑,推荐采用无状态 API 模式,其中 OAuth2.0 能够完全满足。此外 JWT 除了不能满足 SSOff 外,其他都能满足,且是所有方案里最为简便轻巧的一个,可通过搭配 API 网关来满足 SSOff 特性的要求,因此 JWT + API 网关也是一个推荐的方案。

场景假设:构建基于图像的物品识别系统(Image-Based Classification System)
为便于理解统一认证和授权方案的细节,假定一种场景:团队准备构建一套基于图像的物品识别系统,允许用户通过 H5 应用或 APP 上传图像,系统分析后返回识别结果;同时期望将此系统开放给社区和行业用户以便商用;最后允许第三方应用将自身的功能接入 IBCS 以增强后者的能力。
下图是该系统的微服务架构图:
微服务架构:统一身份认证和授权 - 图2
在微服务架构下,IBCS 分为内外两层,内层处于物理内网环境下,也称为内部服务,外层处于物理外网环境下,也称为外部服务,内外通过 API 网关连接。

  • 内部服务:鉴权服务、配置服务、图像识别服务属于内部服务,通常内部服务之间是相互信任的,但在安全要求较高的场景下,内部服务也不能互信。
  • 外部服务:桌面 APP、无线 APP、H5、第三方应用属于外部服务,外部服务分为两种类型:一种是 IBCS 的一部分,如 APP、H5 这些前端应用;另一种是 IBCS 之外的第三方应用。两种类型的授权方式是不一样的,前者一般采用 OAuth2.0 的密码模式,后者则采用客户端模式。

下文将以物品识别系统为例子,介绍这两种推荐方案:

二)最佳方案: OAuth2.0

1. OAuth2.0 的四种授权模式

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

其中密码模式常用于外部服务的鉴权,客户端模式常用于内部服务鉴权和开放平台应用的授权,授权码模式常用于社会化登录和 SSO,因此 OAuth2.0 可作为完整的统一身份认证和授权方案。

2. OAuth2.0 的几种重要角色

必须注意的是,这些角色是相对的概念。

  • 客户端 Client:一般指第三方应用程序,例如用 QQ 登录豆瓣网站,这里的豆瓣网就是 Client;但在微服务体系里,Client 通常是服务本身,如 APP 端的注册登录服务;
  • 资源所有者 Resource Owner:一般指用户,例如用 QQ 登录豆瓣网站,这里的所有者便是用户;但在微服务体系里,资源所有者是服务提供者本身;
  • 资源服务器 Resource Server:一般指资源所有者授权存放用户资源的服务器,例如用 QQ 登录豆瓣网站,这里的 QQ 就是资源服务器;但在微服务体系里,服务提供者本身便是资源服务器;
  • 授权服务器 Authorization Server:一般是指服务提供商的授权服务,例如用 QQ 登录豆瓣网站,这里的 QQ 便是授权服务器;类似地,在微服务体系里,鉴权服务便是授权服务器。

    3. IBCS 提供哪些功能

    1)核心功能,以 API 形式暴露:
    | 接口 | 描述 | body | 返回 | 权限 | | :—- | :—- | :—- | :—- | :—- | | POST /image-classify | 图像识别 | { 图片内容, token } | { 识别结果 } | 受控接口 |

2)由 UIMS 提供的功能:
功能/API 描述 body 返回 权限
POST /accounts/ 注册接口 { username, password } { sign-up-result } 非受控接口
POST /accounts/login 登录接口 { username, password } { token } 受控接口
POST /accounts/logout 登出接口 { token } { logout-result } 受控接口
SignUp-Page 统一注册页面(UI) 非受控页面
Login-Page 统一登录页面(UI) 非受控页面

其中,注册接口、SignUp-Page 和 Login-Page 页面是非受控接口/页面,意味着无须鉴权即可访问。

SignUp-Page 和 Login-Page 页面是由 UIMS 提供的统一的注册和登录页面,当外部服务发起注册或登录请求时,有两种作法:一是统一跳转到 UIMS 的注册或登录页面,用户完成操作后调用 UIMS 的注册和登录 API 完成请求;二是在自己的注册和登录页面完成操作,然后以用户名和密码作为参数调用 UIMS 的注册和登录 API 完成请求。推荐采用第一种方法,类似于全网通行证,用户体验统一,不用重复开发注册登录页面。

3)成为开发者,获取 IBCS 的能力集:
  1. 第一步:申请成为开发者。开发者分为个人开发者和组织开发者两类,实名认证也分两种类型进行。成为开发者的前提是成为平台的注册用户;
  2. 第二步:创建应用,平台审核通过后,生成 AppId 和 AppSecret 给到开发者,每个应用对应一对 AppId 和 AppSecret。值得注意的是,每个 AppID 与用户账号是绑定的,因此每个 AppId 获取资源和能力的权限受到该用户账户权限的限制,典型的例子是对象存储服务(OSS/OBS);
  3. 第三步:获取 Access Token,开发者按照 OAuth2.0 的规范,采用客户端(client_credentials)模式,利用申请到的 AppId 和 AppSecret 向 IBCS 申请 token;
  4. 第三步:使用 Access Token 与平台进行交互,每次调用受控 API 时都携带此 token。
    4)成为开发者,创建第三方应用:
    一般来说,应当独立建设一个开放平台,开发平台作为整个云平台的一个子系统,同样依赖于 UIMS。在开放平台上,应当提供一套完善的界面和流程,以引导用户完成开发者认证和第三方应用接入的所有工作。此外,在前期阶段,也可以采用线下申请的方式,由管理员人工审核,在后台手动录入。
    在开放平台上,创建第三方应用的流程和步骤,与上一步骤『成为开发者,获取 IBCS 的能力集』一致。所不同的是,上个步骤是获取 IBCS 的能力,而本步骤『创建第三方应用』,是基于开放平台开发应用,类似于微信小程序。
    5)成为开发者,将异构系统(第三方应用)接入 IBCS:
    应该允许开发者将异构系统(第三方应用)接入 IBCS,以增强 IBCS 的能力。例如,假设 IBCS 本身不具备识别特种汽车的能力,但允许接入其他开发者开发的基于图像的特种汽车识别应用。
    第三方应用接入,归属于开放平台的范畴。接入的流程和步骤,与第三步骤『成为开发者,获取 IBCS 的能力集』一致,由开放平台提供标准的 API,第三方按照接入规范执行。

    4. OAuth2.0 四种授权模式的应用场景

    | 场景 | 描述 | 适用模式 | | :—- | :—- | :—- | | 用户注册(外部服务) | 用户在 APP 提供的注册页面,完成注册请求 | 非受控接口,无须鉴权 | | 用户登录(外部服务),返回 token | 用户在 APP 提供的登录页面,完成登录请求,获得 token | 密码模式(resource owner password credentials) | | 用户注册(UIMS) | 用户跳转到 UIMS 的注册页面,完成注册请求,注册成功后,跳回到原服务 | 非受控接口,无须鉴权 | | 用户登录(UIMS),返回 token | 用户跳转到 UIMS 的登录页面,完成登录操作,获得授权码,然后携带授权码跳转到重定向URI,再获得 token | 授权码模式(authorization code) | | 外部服务的鉴权 | 用户在 APP 上使用图像识别服务,APP 调用 IBCS 的图像识别 API 并返回结果给用户 | 密码模式(resource owner password credentials) | | 内部服务的鉴权 | 图像识别服务向配置服务获取配置信息 | 客户端模式(client credentials),或简单的 HTTP Basic 验证 | | 开发者:获取 IBCS 能力集 | | 客户端模式(client credentials) | | 开发者:创建第三方应用 | | 客户端模式(client credentials) | | 开发者:接入第三方应用 | | 客户端模式(client credentials) |

5. 客户端鉴权和用户鉴权

服务鉴权,从形式上分为:

  1. 非受控服务/接口,无须鉴权;
  2. 客户端鉴权(服务自身鉴权):客户端(服务)在访问另一个服务时,必须先表明客户端自己的身份;
  3. 业务鉴权(用户鉴权):用户通过客户端(服务)访问某个资源时,必须验证用户自己的身份。

例如,用户通过 APP 登录 IBCS 后,访问图像识别服务的过程:用户登录后获得 token,由 APP 携带此 token 向图像识别服务发起请求,图像识别服务首先调用鉴权服务验证 APP 的身份(通过 ClientId 和 ClientSecret),然后再验证用户的身份(通过 token),如果 APP 和用户都获得授权,则请求通过,返回识别结果。

6. 跨域问题

浏览器的同源策略给 Web 应用划定了安全边界,是 Web 应用安全模型的重要基础。基于令牌的安全系统,在同源策略的约束下面临两个问题:

  1. 跨域请求;
  2. SSO 登录状态的跨域保持。
    1)CORS 方案
    第一个问题,一般采用 CORS 方案,在服务端的响应头声明 Access-Control-Allow-Origin 参数即可解决跨域请求的问题。
    2)同域 SSO 方案
    第二个问题,在同域环境下,传统方法是采用 Cookie 的方案。跨域环境下,也有几种方案,从安全性和简便性考虑,推荐采用这种方案:
  • 根据业务需求将应用切分为同域 SSO 应用和跨域 SSO 应用两类;
  • 将需要 SSO 状态保持的应用归到同域 SSO 应用中,将其他应用归到跨域 SSO 应用中;
  • 对于同域 SSO 应用,一般是企业内部应用,或相关性较高的应用,这些应用的域名采用相同的父级域名,继续使用 Cookie 方案;
  • 对于跨域 SSO 应用,不提供 SSO 状态保持。当用户首次打开此类应用时,由于 Cookie 无法跨域,因此服务端无法感知用户的登录状态,此时用户是处于未登录状态的;当用户访问受控页面或点击登录页面时,须重新执行登录操作。

    7. 登出和关闭账户

    OAuth2.0 是集中式的令牌安全系统,可以通过撤销令牌登出系统。关闭账户与此类似。

    8. 软件授权

    可在鉴权服务或 API 网关增加规则过滤器,将商业授权策略应用到授权规则中。

    9. 技术选型

    后续会写实践篇,敬请期待……

三)第二方案:JWT + API 网关

JWT 是一种自包含的客户端令牌系统技术规范,这是其与 OAuth2.0 最大的不同。除此之外,可以简单地将 JWT 当作 OAuth2.0 密码模式的半自动版本。在实施 JWT 方案时,大部分情况下与 OAuth2.0 基本一致,本文不重复阐述,只对几个要点和不同之处加以说明。

1. 搭配 API 网关实现令牌撤销

由于 JWT 属于自包含的客户端令牌系统,令牌发出后无须服务器验证,只需在客户端验证。客户端验证并解签后将得到必要的信息,例如用户基本信息和权限标识符。这种设计天然地存在无法撤销令牌的问题。解决方案是在 API 网关对 JWT 进行拦截,这里有多种方法:

  1. 令牌撤销由 UIMS 发出,经由消息队列、API 等手段通知到网关,网关维护一个已撤销令牌的黑名单,对所有经过网关的 JWT 进行比对,TRUE 则拒绝转发,并返回 401;
  2. 令牌撤销由 UIMS 执行后,网关每次收到 JWT 请求时,查询令牌数据库(如 Redis),比对该令牌是否已经撤销,如已撤销则返回 401。

不过此方案仍然存在两个问题:

  1. 将一定程度丧失 JWT 客户端解签的优势,但相较于传统的 Cookie + Session 方案,此方案更加轻巧,也更加符合微服务无状态 API 的风格;
  2. 对于已发出的令牌,客户端在下一次请求之前,服务端的令牌系统对此没有控制能力,例如 SSOff 将无法很好地实现。

    2. 客户端鉴权和用户鉴权

    与 OAuth2.0 方案一致,客户端同样需要使用 ClientId 和 ClientSecret 鉴权。

    3. 公钥和密钥

    JWT 规定采用非对称加密算法对 Header 和 Payload 进行签名。

    1)非对称算法

    非对称算法的重要特点是,使用密钥加密时,必须用公钥解密;用公钥加密时,必须用密钥解密。利用此特性,通常在服务端采用密钥加密信息,然后客户端采用公钥解密信息。由于密钥存储在服务端,因此安全性高;公钥本身可以公开,因此可以在客户端存储。

    2)公钥解密

    JWT 经由服务端用密钥加密后,发往客户端,客户端使用公钥进行解密,便得到了 JWT 的明文。JWT 包含了丰富的信息(通常是用户基本信息和权限标识符),只要解密成功,客户端完全可以信任此 JWT,因此不必再依赖于服务端重复鉴权。

    4. 服务间鉴权

    1)内部服务鉴权

    以 IBCS 为例,当图像识别服务服务携带 JWT 向配置服务请求资源时,配置服务使用公钥解密,只要解密成功,配置服务完全可以信任图像识别服务,因此也不必再依赖于鉴权服务的重复鉴权。对于安全性要求不高的场景,也可以使用 HTTP Basic 验证进行简单鉴权。

    2)外部服务鉴权

    同样,当外部的 APP 携带 JWT 向内网的图像识别服务发起请求时,图像识别服务使用公钥解密,只要解密成功,图像识别服务完全可以信任该 APP,有所不同的是,外部服务向内网服务发起请求,必须经过 API 网关,由网关执行规则过滤,确保 JWT 是仍处于有效状态。

    5. 跨域问题

    与 OAuth2.0 的跨域解决方案一致。

    五、总结

    本文给出了微服务架构下的统一身份认证和授权的设计方案,从平台级 SAAS 模式下『统一身份治理』的概念出发,梳理了关键的需求点,提出了对应的解决方案。其中 OAuth2.0 是最佳解决方案,不过在实际运用中,应当遵循『合适原则』、『简单原则』和『演化原则』三个原则,不能盲目照搬。

    六、参考链接

  3. SAAS,http://www.ruanyifeng.com/blog/2017/07/iaas-paas-saas.html

  4. SSO, https://www.cnblogs.com/EzrealLiu/p/5559255.html
  5. CAS, https://apereo.github.io/cas/
  6. UIMS, https://mtide.net/平台级SAAS架构的基础-统一身份管理系统.html