0%

统一认证中心方案调研

为什么需要认证中心

认证(Authentication)和授权(Athorization)是企业级服务的基本功能,企业级应用的开发必须考虑到如何使提供的产品与用户已有的认证和授权体系结合起来,更进一步能够基于该体系提供开发接口给其它应用调用。如下图所示,不同的用户可能已经有不同的认证体系,有的用户可能用的是
LDAP,这是种比较老的认证方式,SAML 新一些,最新的是
OpenID,这些不同的认证后端都应当可以接入到统一认证中心,从而能够更好地管理用户和进行授权。

image.png

OpenID

如上图所示,不论用户已有的认证体系是基于什么协议的,通过认证中心我们将其统一转换为 OpenID 协议从而方便应用服务的开发,协议的转换可以交由 Keystone 和 Keycloak 等开源方案来解决, 而 OpenID 是我们开发环节必须要涉及到的内容,因此需要搞清楚其原理。
OpenID 是 OAuth 2.0 协议的扩展,主要增强了认证功能,因此认识 OpenID 需要从 OAuth 2.0 开始。OAuth 2.0 协议的应用实际上已经相当广泛,现在很多网站都允许使用微信扫码登录,这个登录过程其实就是 OAuth 2.0 协议的一种实现。下面以图灵社区使用微信账号登录为例说明整个过程:

  1. 第一步:打开图灵社区登录页面https://account.ituring.com.cn/log-in,点击微信登录图标

image.png

  1. 第二步:页面跳转至微信认证中心
1
https://open.weixin.qq.com/connect/qrconnect?appid=wx3a6700225b70af1f&redirect_uri=https%3A%2F%2Faccount.ituring.com.cn%2Fsignin-wechat&response_type=code&scope=snsapi_login&state=7nh23BdGirIHwjyH79bzV6YjF-55Vn7AzzZIjFZZnKrFxCmAsisTRfSCD7ikv_PMx7uFjsdQPUA8A94MzuZxPOAAvqd1P-0-oo2HGw0yatvKc4hZFU0-ZpEBHt2gqkXqXHAI-0wpyATNbXv14mDzcJSPR-OST90-XwoHpwD0fCXlM3sGdzYHHHPb74bW4dMHSeOThBkhpJvD6oE_k0TFAwQd4cIFV9-gj5tYRelAawc

image.png

  1. 第三步:用户微信扫码进行授权确认

微信图片_20190910155905.png

  1. 第四步: 登录成功,返回图灵社区主页。

这里的四步是我们能够直观看到的步骤,实际上背后的交互逻辑如下:

auth-center-02.png

交互过程中涉及到的 OAuth 术语有:

  • Resource owner:: 用户
  • Client:需要授权的应用,这里指图灵社区
  • Authorization server:认证中心,这里指微信开发中心
  • Resource server: 拥有 Client 所需资源的服务端,这里指存储有用户基本信息的微信 API 服务端
  • Access token:Client 与 Resource Server 交互时的凭证
  • Scope: 通常指一个可用 api 资源的集合,这里的 scope 值为 snsapi_login 意味着 client 请求的这个集合只包含登录用的基本操作 API ,如查询用户基本信息,而不能去操作用户,如更改用户名
  • Consent: 同意,这里指用户同意授权图灵社区获取自己的基本信息

更具体的交互过程参考:

Keystone 与 Keycloak

Keystone 是 OpenStack 社区的统一认证项目,主要是作为 OpenStack 多租户管理的基础组件 ,由于 OpenStack 所有项目的设计都具有松耦合的特点, Keystone 项目完全可以单独拿出来使用,另外 Keystone 也充当了 OpenStack 项目的服务注册中心。
Keycloak 是 RedHat 维护的统一认证项目,是其商业产品 RH-SSO 的上游项目。
下面对两个项目做对比说明。

  • 基本信息

下表显示的基本信息为 2019 年 09 月 15 日的情况:

| 比较项 | Keystone | Keycloak
|
| :—: | :—: | :—: |
| 开发语言 | Python | Java |
| 开源 | 是 | 是,有商业版 |
| 维护方 | OpenStack 社区 | RedHat 及社区 |
| 贡献者数量 | 433 | 328 |
| 提交数 | 14250 | 11665 |
| 最近一年的提交数 | image.png | image.png |
| LDAP 集成 | 支持 | 支持 |
| 支持的数据库 | MySQL | h2,MySQL,Postgres |
| UI | 无独立 UI,界面集成在 Horizon 中 | 有独立 UI,可以定制主题以适应需要 |
| SDK
| 不区分 Admin 和 普通用户 SDK ,js SDK 有但不完善, Python SDK 最完善,Go SDK 相对完善。 | 区分 Admin 和 普通用户 SDK ,Admin SDK 提供了管理用户、角色和应用认证等的功能,普用户 SDK 主要实现认证和授权功能, Admin SDK Java 版本最完善, Node 版次之, Go 版为非官方维护,较为完善。普通用户 SDK 同样是 Java 最为完善,其次是 JavaScript 、Node、Python 等,由于其遵循标准的 OpenID 认证流程,故各种语言已有的 OpenID Client 都可以直接使用。
|
| Rest API | v3 版本文档:https://docs.openstack.org/api-ref/identity/v3/ | 7.0 版本的 Admin API 文档
https://www.keycloak.org/docs-api/7.0/rest-api/index.html |
| 基于 Kubernetes 部署 | 官方有使用 Helm charts 部署整个 OpenStack 集群的方案,没有专门的使用 Kubernetes 部署 Keystone 的方案,可从 OpenStack helm charts 中抽离,但有一定的工作量。
| 有非官方的 Helm charts 用于部署高可用 Keycloak 集群,经试用满足需求。另外最近官方新开了 keycloak-operator 仓库,下一步有可能提供基于 CRD 的部署支持。
|
| 非侵入式认证 | 未见官方支持方案 | 官方提供 keycloak-gatekeeper 项目通过 proxy 方式代理前端请求,认证过程由代理完成,业务代码中不需要加入认证处理的内容,方便现有业务的无侵入接入认证。可以结合 Nginx-Ingress 等 API Gateway 使用。
|

  • 基本概念

Keystone 和 Keycloak 有一部分概念是相同的,有一部分是相似的(即涵义基本相同,名称不同),还有一部分是各自独有的,下表对照说明:

| 比较项 | Keystone | Keycloak
|
| :—: | :—: | :—: |
| 用户 | Users:用户必须属于某一个域(domain),用户名非全局唯一,而是域内唯一。 | Users:概念一致。 |
| 用户组 | Groups:用户组包含一组用户,用户组必须属于某一个域(domain)。 | Groups:概念一致。 |
| 项目 | Projects: OpenStack 中为方便多租户管理的概念。OpenStack 中的虚拟机、虚拟盘等资源必须是属于某一个项目,而项目本身必须是属于某一个域(domain)。
| 无 |
| 域 | Domains: 域是项目、用户组、用户的容器,Keystone 提供默认域(Default)。对所有用户、项目和用户组的操作都必须提供域信息。域名全局唯一。 | Realms:和 Domains 概念一致,Keycloak 提供默认域(Master)。 |
| 角色 | Roles:用于约束用户在某种资源上可以进行的操作,从而实现基于角色的授权(RBAC)。可以对单个用户或某组用户授予操作某个项目或某个域的某种角色。角色名域内唯一。 | Roles: 概念一致。
|
| Token | Token:用于在向资源服务器请求资源时标示自身身份。根据用途的不同和有效范围可分为多种 token。 | Token:用途一致,实现 token 使用的算法有所区别 |
| 服务目录 | Catalog:服务目录,用于集中注册和管理 OpenStack 各种服务,方便松耦合的各项服务之间互相发现和调用。
| 无 |
| 客户端认证 | Application Credentials:应用凭证,与用户绑定的一种凭证,拥有与用户一致的权限(Role),也可以在创建时指定,专门给 client 端在无需使用用户和密码进行登录时使用。
Credentials:与用户名和密码基本等同的凭证,只不过是以 EC2 方式提供的凭证,包括 accekey 和 secret key。 | Clients: 和 OAuth 术语基本一致,只能由管理员用户创建,和普通用户并无绑定关系。使用标准认证协议 OpenID(OAuth 2.0)或者 SAML2 。可以配置成需要用户登录认证或者直接访问。 |
| 认证提供方 | Identity Provider:第三方认证提供方,用于替代 Keystone 的认证功能, 例如 Github、微信等,只要是支持的标准协议就可以接入, Keystone 支持 SAML2 和 OpenID
。Keystone 既可以接入第三方认证提供方,其本身也可以作为认证提供方供其它服务调用。当作为认证提供方时支持 SAML2 和 OAuth1.0a 对外服务,不支持 OpenID。
| Identity Provider:概念一致,但是作为认证提供方时支持 SAML2 和 OpenID。
|

  • 结论

通过对比,可以知道 Keystone 更多是为 OpenStack 专门设计的,虽然也支持标准协议的接入,但这并不是其核心和重点功能,而 Keycloak 一开始就是作为独立的认证中心进行设计的,完全建立在标准认证协议之上,并为此提供了完善的周边工具,因此选择使用 Keycloak 作为基础进行认证中心的实现更合适。

引入 Keycloak 后的架构

  • 引入 Keycloak 之前的架构

这是最初的架构,客户端请求经过 API 网关分发到不同的服务,而服务在业务代码中维护用户的认证和授权过程,并且用户的管理包括用户、角色以及用户角色的绑定都由后端服务自己进行。

image.png

  • 引入 Keycloak 之后

由于我们的服务都是基于 Kubernetes 部署的,我们使用了 ingress-nginx 作为 API 网关, ingress-nginx 可以视为 nginx 为 Kubernetes 定制的版本,功能受到 Kubernetes Ingress 定义的限制,虽然支持使用 OAuth 2.0 对接外部认证服务,但本身并不支持对接外部授权服务。如果使用 ingress-nginx 对接 Keycloak 认证,则总体架构变为下图所示,Ingress 负责进行认证,各个后端服务仍然需要自行去处理授权。
image.png
更进一步,使用 keycloak-gatekeeper 进行认证和授权,这样后端服务将完全和认证与授权解耦,只关注业务逻辑,这样解耦的一个好处是,更换认证提供方对于后端服务是无感的,更加灵活。
image.png

  • keycloak-gatekeeper

keycloak-gatekeeper 是 Keycloak 官方使用 Go 语言开发的请求代理转发组件,集成了认证和授权功能,对服务的请求只有经过了 gatekeeper 的验证才会被转发到下一环节,否则将直接返回未授权响应。一个示例 keycloak-gatekeeper 如下, 对于 /admin 路径下资源的请求必须具有相应的角色才能继续进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# is the url for retrieve the OpenID configuration - normally the <server>/auth/realm/<realm_name>
discovery-url: https://keycloak.example.com/auth/realms/<REALM_NAME>
# the client id for the 'client' application
client-id: <CLIENT_ID>
# the secret associated to the 'client' application
client-secret: <CLIENT_SECRET>
# the interface definition you wish the proxy to listen, all interfaces is specified as ':<port>', unix sockets as unix://<REL_PATH>|</ABS PATH>
listen: 127.0.0.1:3000
# whether to enable refresh tokens
enable-refresh-tokens: true
# the location of a certificate you wish the proxy to use for TLS support
tls-cert:
# the location of a private key for TLS
tls-private-key:
# the redirection url, essentially the site url, note: /oauth/callback is added at the end
redirection-url: http://127.0.0.1:3000
# the encryption key used to encode the session state
encryption-key: <ENCRYPTION_KEY>
# the upstream endpoint which we should proxy request
upstream-url: http://127.0.0.1:80
# additional scopes to add to add to the default (openid+email+profile)
scopes:
- vpn-user
# a collection of resource i.e. urls that you wish to protect
resources:
- uri: /admin/test
# the methods on this url that should be protected, if missing, we assuming all
methods:
- GET
# a list of roles the user must have in order to access urls under the above
# If all you want is authentication ONLY, simply remove the roles array - the user must be authenticated but
# no roles are required
roles:
- openvpn:vpn-user
- openvpn:prod-vpn
- test
- uri: /admin/*
methods:
- GET
roles:
- openvpn:vpn-user
- openvpn:commons-prod-vpn

扩展阅读资料

深入聊聊微服务架构的身份认证问题

本文到此结束  感谢您的阅读