系统行为之警卫局 - 登录和授权

在现实生活中,身份识别无处不在。 去银行取钱,得带上银行卡。 去公司上班,得带上门禁卡。想访问一个网站,需要先注册,才能访问其内容。
任何一个系统,无论是软件系统,还是真实的现实世界的组织,都需要对人进行识别,然后才能决定这个人能干什么。

  • 背景
  • 什么是登录和授权
  • 如何进行登录和授权
  • 登录和授权相关的实践
  • 登录和授权的未来

背景

登录和授权是一个软件系统里最常见的行为。
还记得在第一家公司使用过一个ERP系统,登录进去只能看到自己所属的模块.
如今应用多了,一个企业内部,可能有专门的财务软件,也有可能有外部购买的软件,基于时间就是金钱,效率就是生命的原则,在一家公司内部,如何统一管理用户的账号,在一个地方登录了系统A, 在另外一个地方可以直接登录系统B,成为一个企业必须解决的问题(SSO).
我要登录极客时间,有好几种方式,但最省时的方式肯定的是第三方登录,点击微信图标, 唰唰几步就完成了登录,而不是重新注册一个账号,输入电话,电子邮件。这也体现了用户至上的设计理念。 换句话说,没有人喜欢复杂和繁琐的东西。简单,简单,还是简单.

什么是登录和授权

登录和授权是两件事,要分开说.

什么是登录

登录是指一个主体可以进入系统S的行为. 这里的主体可以是人或者是一个物体,系统S可以是软件,也可以是指一个组织
以下是一些登录的例子

  • 打开一个web,输入用户名和密码 - 这个是最常见的行为,也是所有验证模型的雏形.
  • 张三刷门禁进公司
  • 插入银行卡,输入密码取钱

登录的形式

  • 输入用户和密码
  • 输入用户和密码,再加上n重验证(比如验证码,或者回答一个私密问题)
  • 按指纹
  • 面部识别
    从这里看出来,登录这个行为隐藏了一个主题:更简单的方式追求更高的安全

什么是授权

授权是指一个主体进入系统S之后,它在系统S里面能做什么. 这里的授权是广义上的授权,和之后讨论的oAuth的概念稍微有点不一样
授权的例子如下

  • 登录一个电商网站之后,可以下单买东西
  • 进入公司内部,可以做在自己的座位上,不能坐在别人的座位上。
  • 登录进入一个ERP系统,我是销售部门的人,只能看到销售模块,不能看到财务模块.

授权这个行为表明了主体在系统S里面基于特定的约束,只能做特定的事情.

如何进行登录和授权

单系统的登录和授权

单系统登录设计原则

  • 密码需要设计复杂度
  • 存在数据里的密码需要加密
  • 最好加一些额外的验证 - 比如验证码
  • 能以最小的成本平滑的过渡到SSO

单系统授权设计原则

  • 权限表能很好的添加或者删除权限
  • 有专门的工具来维护权限的添加和删除

多系统登录和授权相关规范

下面是一些有关登录和授权的规范,我之所以称它们为规范是为了便于方便讨论。 因为地方会称这些规范是个协议,或者是个框架,或者从字面上看是个语言,这样概念太多,会让人迷糊不清。
规范本质上是一种约束。 它描述了

  • 一个系统可以做什么
  • 一个系统不可以做什么
  • 一个系统如何做某件事

SAML

SAML(Security Assertion MarkUp Language)顾名思义是一个与安全和断言有关的规范。 下面引用了维基百科的定义

SAML is an open standard for exchanging authentication and authorization data between parties, in particular, between an identity provider and a service provider. SAML is an XML-based markup language for security assertions (statements that service providers use to make access-control decisions).

维基百科回答的大体还是准确,但一会说这是个标准,一会又说这是一个语言,所以很乱。
从这里也可以看出, SAML主要解决这样一个问题: 如何在IDP和SP之间交换验证和授权的信息?
SAML有三个角色

  • Client - 张三
  • Identity Provider - 微信
  • Service Provider - 极客时间


基于上面三个角色, SAML大体的workflow是

  • Client 访问 Service
  • Service 将 Client 导向到 IDP
    • Client 登录成功
  • IPD将Client导向到 Service
    • Service验证用户登录成功
  • Client在浏览器上可以正常访问资源

OAuth

OAuth是一个关于授权的标准,它不做验证。

OAuth 2.0 is the industry-standard protocol for authorization. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices

OAuth的的角色

  • Resource Server - 资源,比如房子
  • Resource Owner - 资源的拥有者,比如这个房子是属于张三的
  • Authorization Server - 有个组织负责给张三配房子的临时钥匙
  • Client - 李四

OAuth的故事大概是这样的:
张三有一家公司,李四想去张三的公司仓库里取货,但张三不能把真正的钥匙给李四,只能委托一个机构给李四一把临时的钥匙,这把临时的钥匙只能去仓库,不能去财务室. 这把临时钥匙是不记名的,换句话说,如果王五抢劫到这把临时钥匙,也是可以去仓库取东西的.

更技术一点就是说

  • 授权服务器如何采取一种简单又安全的方式给Client生成access token?

OAuth的花式生成access token的方式

  • 授权码
  • 简化模式
  • 密码模式
  • 客户端模式

OpenId Connect(OIDC)

OpenId Connect是一个专注于身份验证的规范.

OpenID Connect is an interoperable authentication protocol based on the OAuth 2.0 family of specifications. It uses straightforward REST/JSON message flows with a design goal of “making simple things simple and complicated things possible”. It’s uniquely easy for developers to integrate, compared to any preceding Identity protocol.

从上面的描述可以知道 OIDC也包含授权功能,因为它是基于OAuth2.0, 但我们要记住OIDC的主业是认证,认证是它的价值主张, 授权只是它的副业.
如果说授权最终的产出是access token, 那么验证最终的产出就是id token. 基本上,只要拿到了这两种token, 就可以在各个软件系统中为所欲为的横着走了.

OIDC的角色

  • End User - 张三
  • Relying Party - 相当于OAuth的Resource Server。 比如极客时间
  • OpenID Provider - 身份认证服务,类似于SAML中的IDP。 比如微信
  • ID-Token - 包含身份认证信息的JWT
  • UserInfo Endpoint - 获取用户的昵称和头像等信息

OIDC的大体的WorkFlow是这样的

  • RP 发送认证请求给 OP
  • OP 让用户来验证
    • 用户提供正确的身份验证
  • OP 生成 id token 和 access token给RP
  • RP 根据 access token 发送一个获取用户信息的请求.
  • RP 获得用户的信息

相关框架

Spring Security

Spring Shiro

KeyCloak

登录和授权的相关实践

如何设计一个权限系统

这里的权限系统是指一般意义上的权限:主体对资源能执行什么样的操作? 比如张三可以文件执行写操作.
所以权限设计是为了解决这样一个终极问题

  • 主体在什么样的条件下可以对资源执行什么样的操作, 而且随着用户的增加,添加权限的操作尽可能的简单和更安全。

一些权限模型

  • ACL - 基本的思路是:对一个文件A需要配置张三读写权限,文件B李四需要配置写权限
    • 这种配置是符合直觉的,但是随着用户量的增加,需要一个一个的配,很繁琐.
  • DAC (Discretionary access control) - 是基于 ACL的扩展。 它引入了组和给其他主体授权的概念
    • 张三可以具有对文件A读写的权限,张三属于销售组,所以销售组也可以对文件具有读的权限
    • 文件A的拥有者张三同时也可以将文件A读的权限赋给李四,这样是自主的核心要义。
    • Windows和Linux权限是基于DAC的。
      • Windows的权限设计不够灵活,比如说“读取和执行”是一个权限,但其实是微软将读取和执行两个权限打包在一起了,然后让用户选择允许和拒绝
      • Linux的的权限设计充分体现了细粒度,互斥的组合的思想。比如说权限只有三种,读,写,执行,一个文件有三大类用户: 拥有者,所属组,其他人。每大类用户有7中权限,所以总的权限组合是777
  • MAC (Mandatory access control) - 相比于DAC, MAC强调的是更安全。张三和李四同属于销售组,但是张三是经理,李四是员工,所以张三和李四对文件A是有不同对访问权限的,这个问题DAC就解决不了. MAC引入了信息敏感度这个概念, 也就是引入了一个新的维度.
    • 要配置资源的信息等级
    • 要配置用户主体的信息等级
    • 核心的财务文件只有经理级别的财务人员才能查看,但是经理级别的销售人员就不能查看.
  • RBAC (Role based access control) - RBAC的核心概念是:主体,角色,资源
    • 一个销售角色有很多权限,比如查看合同,创建客户,删除客户等等,只要一个人是销售,它就具有这些权限
    • 角色是权限的集合,而组是用户的集合
  • ABAC (Attribute base access control) - ABAC相比于其他模型是它的表达力是最强的. 如果要表达张三上午可以对文件进行读取操作,那么上述所有模型都无能为力。ABAC可以表述为: X在Y条件对Z执行A操作, 这里的X,Y,Z,A都是可以自定义的.
    • 来自北京的张三在上午10点钟可以读取服务器上80端口的内容。 这段表述够复杂了吧,ABAC就可以干这事情。已经有点声明式编程的味道了.

操作系统是如何存储用户信息的?

* 协议
  * X.500 - X.500是基于OSI的目录访问服务,众所周知,OSI现在已经被淘汰.
  * LDAP(Lightweight Directory Access Protocol) - LDAP是一个基于X.500的目录访问的协议,但它更简单,而且支持TCP/IP,这对互联网访问非常重要.
* 实现
  * Active Directory - 这是LDAP在windows上的实现,它的层次结构依次是:域(Domain) -> 组织单位(Orgnization Unit) -> 群组(Group) -> 用户(User)
    * 一般来说,AD适合在内网中的C/S架构
  * OpenLDAP
  * ADFS - ADFS是一种跨网络的身份认证方案,也就是用户账户和应用程序位于不同的网络.
    * 相比于传统的AD, ADFS可以穿透不同的网络.
  * Azure AD - 是基于云上的身份认证和授权方案。
    * 它支持Rest风格. 具体一点就是通过api可以拿到access token和id token, 这是开发者最关心的信息.
    * 它支持多重身份验证
    * 它支持多租户,租户和租户之间的数据是彻底隔离的
    * 它不是主域控制器

登录和授权的未来

无论是SAML, OAuth 还是 OpenId Connect, 我希望将来只有一种认证和授权协议, 姑且命名为XAuth.
它有如下特征

  • 生成和解析token是简单的
  • token是安全的
  • 形成规范
  • .net, java以及前端有成熟框架和社区支持,且这些框架被大多数公司所使用.

所有技术无论是原则,模式,还是方式,统一是大趋势。 我们不想要两个或者多个。
可以看到一些例子

  • 前些日子, .net5发布了,统一了.net core, mono.
  • Spring Cloud想统一解决分布式领域内的所有问题,并形成规范.

这也就是为什么我对认证和授权协议会有统一的构想的原因。