# IAM？认证？授权？傻傻分不清楚

经常会听说一些和鉴权相关的名词，像什么JWT、SSO、OAuth2、RBAC、IAM什么什么的，
虽然每一个都知道它们是什么，但是它们之间的关系和区别就不是那么清楚了。

有时候知识体系比较零散，不知道从哪里开始学习，所以就写了这一篇文章，把这些名词串起来，帮助自己理清思路。
其中有些我认为比较复杂或者容易混淆的概念，我会着重说明一下。

本文主要围绕IAM、认证、鉴权这些概念进行讲解，介绍它们相关的实现方式与名词。如果有什么错误与不足，欢迎指正。

## 身份和访问管理（IAM）
> [什么是IAM](https://www.cloudflare.com/zh-cn/learning/access-management/what-is-identity-and-access-management/)，这里有一篇文章，我觉得是不错的。

身份和访问管理（IAM）是一种安全性的框架，用于确保适当的人员能够访问适当的资源，同时防止未经授权的人员访问敏感资源。像阿里云（RAM）、腾讯云（CAM）、AWS等云服务商都提供了IAM服务，可能你也经常使用，只是他们的名字不一同而已。

IAM主要包含两大块：认证）和授权，这两个概念是不同的，但是又是相辅相成的。

### 认证（Authentication）

认证经常简写为Authn，是指确认用户的身份，确保用户是其声称的那个人。

### 授权（Authorization）

授权经常简写为Authz，是指确定用户是否有权限访问某个资源。

### 审计（Audit）

一个完善的IAM系统，审计是很重要的，用于记录用户的操作，方便便追溯问题。

## 登录和认证

登录应该算是认证流程的一部分。

登录是用户行为，是用户开始认证流程的动作。在这个阶段，用户通过提供身份信息（如用户名和密码）来请求访问系统。

认证是系统对用户提供的身份信息进行验证的过程。认证的目的是确保用户是其声称的那个人。如果认证成功，表明系统已经确认了用户的身份。

一般来说，认证成功后会颁发一个令牌给用户，用户可以通过这个令牌来访问系统的资源，这块应该是属于会话管理的范畴。

### 登录

按照上面的定义来说，下面这些场景都可以称为登录，都是用户提供身份信息的过程，
这里也只是列举了一些常见的登录方式：

#### 用户名密码/扫码/指纹/人脸登录

这些都是用户提供自己的身份信息，系统验证后确认用户身份的方式。

##### 多因素认证（MFA）

多因素认证是指在用户登录时，除了提供用户名和密码外，还需要提供其他因素来确认用户身份。

常见的多因素认证有手机验证码、邮箱验证码、指纹、人脸、动态密码（OTP），
总的来说，就是提供多种身份信息来确认用户身份，可以和其它登录方式组合使用。

#### OAuth2/OIDC（三方应用登录）

OAuth2 是一个开放标准，允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息，而不需要将用户名和密码提供给第三方应用。

OIDC 是在 OAuth2 的基础上，增加了用户信息的获取。

例如，用户可以通过 GitHub 账号登录某个网站，这个网站会跳转到 GitHub 的登录页面，用户输入用户名和密码后，GitHub 会返回一个令牌给这个网站，这个网站就可以通过这个令牌获取用户的信息。

#### SSO（单点登录）

SSO 是一种让用户只需要登录一次就可以访问多个应用的技术。
用户登录后，系统会颁发一个令牌，用户在访问其他应用时，只需要携带这个令牌即可。

例如，淘宝和天猫，在登录淘宝后，用户可以直接访问天猫，而不需要再次登录。甚至其它阿里系的应用也可以直接登录。

SSO更多时候是同一个公司内部的应用之间的登录，而OAuth2更多是不同公司之间的应用登录。

而且SSO更多是一种思想，具体实现方式有很多种，比如CAS、JWT等，OAuth2也可以实现SSO。

### 会话管理

当认证成功之后，我们需要颁发一个令牌给用户，用这个令牌来维护用户的会话。否则用户每次请求都需要重新认证。

会话我们可以使用 Cookie、Session、Token 等来实现，常见的有：

#### Cookie

Cookie 是服务器发送给浏览器的一小段数据，浏览器会保存在本地，每次请求都会带上这个数据。

我们可以在 Cookie 中存储用户的身份信息，严格来说，我认为Cookie只是一个载体，你可以把令牌放在Cookie里，也可以放在其他地方，只要能够保证用户的会话即可，通常结合其它技术一起使用。

#### Session

Session 是服务器端保存用户信息的一种机制，用户登录后，服务器可以生成一个Session，把用户的信息保存在这个Session中，然后把这个Session的ID返回给用户，用户每次请求都带上这个Session ID。

Session 可以保存在数据库中，当然也可以保存在 Redis 等缓存中。通常Session都是以 Cookie 的形式返回给用户。

我认为只要是只返回一个ID，而不是用户的身份信息，用户的身份信息都保存在服务器端，都可以称为Session，哪怕你叫这个ID为Token也好。

#### JWT

JWT（JSON Web Token），是一种开放标准（RFC 7519），用于安全地在两方之间传输信息，主要包含`header.payload.signature`三部分，使用`.`进行分割。这种方式也不止JWT，应该对应了所有的无状态Token模式。

其中数据都是以明文形式存在，不加密，使用签名来保证数据的完整性和防篡改，所以不要在 JWT 中存储敏感信息。

JWT本身是无状态的，也就是说服务器不需要保存用户的会话信息，只需要验证JWT的签名即可。

也因为这个特性，JWT无法主动失效，只能等待过期时间到了之后，才会失效，密钥也是不能泄漏，否则可以随意伪造。

我觉得JWT是不适合来做会话管理的，在有主动失效之类的需求时，又不得不依赖数据库等其它方式来实现，倒是变成了像Session一样的东西。

不过也看实际情况，另外JWT也广泛用于：SSO、分布式系统、API认证等场景。

#### 双Token

这其实和上面的没啥关系，只是我觉得可以放在这里说一下。

双token是指用户在登录后，服务器会返回两个Token给用户，一个有效时间较短（例如：2小时）的访问token`access_token`，一个有效时间长（例如：30天）的刷新token`refresh_token`。

每次请求时只发送`access_token`，`refresh_token`只保存在客户端（例如：localStorage），当`access_token`过期时，客户端会发送`refresh_token`给服务器，服务器会返回一个新的`access_token`给客户端并失效老的。

这样做主要是为了保障安全性和用户体验，`access_token`有效时间短，尽量减少泄漏风险，而`refresh_token`有效时间长，用户不会频繁使用。用户体验上，用户10多天都没有操作，也不用重新登录。

感觉APP常用这种方式，网页的会少一些。

## 鉴权

鉴权主要是确定用户是否有权限访问某个资源，是在认证成功后的一个环节。

如果A部门可以访问B部门的私有资源，那么岂不乱套了。

鉴权的实现方式有很多种，常见的有：ACL（访问控制列表）、RBAC（基于角色的访问控制）、ABAC（基于属性的访问控制）等。

简单的IAM系统可能只需要一个ACL就够了，复杂的更多是用RBAC和ABAC。

像公司的OA系统，一般都是RBAC；各大云服务商的IAM，一般都是ABAC，
像阿里云RAM、腾讯云CAM，都抽象出来了：用户、用户组、策略、角色等概念，虽然是ABAC，但是看起来很像RBAC，使用起来也更方便。

像ABAC，一般都是使用像下面的策略语言来描述：

```json
{
    "version": "2.0",
    "statement": [
        {
            "action": [
                "dnspod:CreateRecord",
                "dnspod:DescribeDomain",
                "dnspod:CreateDomain",
                "dnspod:DescribeRecordList",
                "dnspod:DeleteRecord",
                "dnspod:ModifyRecordStatus"
            ],
            "effect": "allow",
            "resource": "*"
        }
    ]
}
```

ABAC可以更灵活的控制权限，但是也更复杂，像上面的，如果我希望某个用户只能控制某一个域名，那么我可以在`resource`中指定，而RBAC就不太好实现了。

## 最后

如果你想设计一个IAM系统，希望本篇文章能够对你有所帮助。若有什么错误或者不足，欢迎指正。
