最近在看了一点oidc
相关的协议内容,想以此来做单点登录。但看了协议,还有好多不太明白的地方。
oidc
相比oauth2
来说, 多了一个id_token
,用于身份认证,而本身oauth2
部分的又有access_token
及refresh_token
,access_token
是用于保护资源的,refresh_token
是用于刷新access_token
的.
那举个简单的使用场景:
一个平台,有以下几个服务,
统一身份认证的服务identify
(基于oidc
协议实现的idp
)
服务 A(a.com)
服务 B(b.com)
那在 oidc 体系中,a.com
, b.com
实际上是两个应用(client)
正常的流程就是:
a.com
判断本地是否有token
(基于localstorage
是否有相关的存储),
如果有,则每次正常请求携带token
,
如果没有,则携带 client 信息(client_id,redirct_uri,scope 等)跳转至identify
服务的登录页面,
identify
服务会对用户的用户名密码认证,认证成功后跳转至a.com/callback?code=xxxxx&state=xxx
a.com
根据code
码获取到id_token
,access_token
和refresh_token
那么有以下疑问:
问题 1: 前端中header
头上使用的authorization
是塞id_token
还是access_token
? 按照定义,应该是id_token
吧。
那access_token
的作用是不是如果我的资源只是限定有没有登录,登录就可以访问所有的资源(api 的鉴权有另外的鉴权服务),那其实access_token
在这种场景下就没有意义?
问题 2:refresh_token
的作用是用来刷新id_token
还是access_token
? 因为refresh_token
, access_token
是 oauth2 就有的概念,最开始 refresh_token 肯定是刷 access_token 的,
那如果使用id_token
,因为 jwt 的签发后无法回收,id_token
理论上也是时效设置成比较短的一个时长,那这样必然是需要 refresh_token 去保证自动时长续期,所以可以使用返回的 refresh_token 值去
刷新 id_token 么?
问题 3:在多应用场景的单点登录下,比如a.com
按照以上流程登录了,那理论上在同一个浏览器下,访问b.com
应该也是登录状态的。那这个登录状态是怎么判断呢?b.com
的本地存储里肯定是没有存储 id_token 的。
那此时,是不是在a.com
登录的时候,登录成功其实就应该在identify
的前端的localstorage
里存储id_token
?那这样问题又来了,刷新token
了怎么办? 刷新 token 一般在应用端发起,
但又影响不到identify
服务的前端存储. 没有想到比较完美的闭环
1
tool2d 2023-10-16 14:25:45 +08:00
|
2
nobject OP @tool2d 谢谢,看这图明白了点,id token 是用于认证,access token 是用于 api 访问权限判断。那我请求一个 api ,如果后端去解析 id token 的 jwt ,那前端不是得把 id token 与 access token 一并传给后端?
|
3
tool2d 2023-10-16 15:28:27 +08:00
@nobject 大部分情况只传一个 access token 吧,都是授权给第三方 app 访问的,很容易就过期了,需要刷新。
以前 access token 不规范,现在很多公司用 JWT 格式,个人感觉和 id token 很接近了。 |
4
nobject OP @tool2d 开放给第三方应用的确实得传 access_token ,但我现在的应用场景是单点登录,我们一个大平台,里面可能有各个子系统,这些系统其实都不算是第三方的应用,理论上是属于我们公司自己的产品,oidc 这边其实就是要做一个统一认证平台,在 api 的授权访问这块,其实有 rbac 的鉴权模块统一处理的。从需求方面,我觉得我们可能用的是 id_token, 不是 access_token
|
5
FuryBean 2023-10-16 15:59:11 +08:00
|
6
FuryBean 2023-10-16 16:04:23 +08:00
|
7
8rmEHZ8WhVHVOb0E 2023-10-16 17:25:15 +08:00 via Android
access_token 是给你的 a 应用和 b 应用的后端用的,而不是前端,你的 A 应用前端和 A 应用后端交互你需要自己实现。
a 站点登录后 b 站点也自动登录,这个在 cas 和 OIDC 中并没有统一实现,都是你访问要登录的页面时,才跳转到 sso 登录。如果你某个页面,没有登录也可以访问,但是如果 sso 登录了,需要同步登录,常见做法,假设域名为 sso.domain.com a.domain.com c.domain.com sso 登录时,在根域名 domain.com 下写两个 cookie ,一个写明文明文 UID ,一个写 UID 加密字符,应用 a 和 b 后端随时检测这两个 cookie 是否存在,如果存在,且检测加密是否为你的系统生成的 cookie 而不是用户自己乱写入的,则跳转到 sso 认证再跳回当前浏览页面,即可实现非必须登录页面的自动同步登录状态,同理,可以用该 cookie 实现同步退出,a 应用退出后跳转至 sso 退出,sso 删除这两个 cookie ,访问已经登录的 b.domain.com ,该站点检测到根域名下的 cookie 已经失效,清楚当前站点登录状态。 如果应用和 sso 不在同一个域名下,例如: sso.qq.com www.qq.com www.paipai.com 此时,paipai 站点无法读取到 qq.com 下的 cookie ,并不知道用户已经在 sso.qq.com 下登录了,需要手动跳转至 sso.qq.com 认证。常见方案是,sso.qq.com 登录后 iframe 或者 jsonp 方式调用 sso.paipai.com ,sso.paipai.com 会同步在 paipai.com 根域名下写 cookie ,www.paipai.com 检测到这两个 cookie 存在且合法的情况下跳转至 sso.qq.com 认证。 |
8
8rmEHZ8WhVHVOb0E 2023-10-16 17:34:16 +08:00 via Android
@FuryBean OIDC 和 Cas 其实都适合内部 sso ,因为 git 、oa 等很多内部应用是第三方厂商开发的,所以需要一个开放统一的认证协议。如果你的 sso 是 to c 的话,不如自定义 sso 协议来的方便。你可以观察 qq 淘宝这些大厂商,他们做起来的时候,还没有这些玩意,所以他们的 to c 的账户系统都是自定义协议的,而小米、vivo 、oppo 的 sso ,你可以去登录页面观察参数,依旧没有使用 cas 和 oidc ,而是选择了自定义的方式,不过原理都是差不多的。
但是这些厂商的内部员工用的 sso 都支持 cas 这些,应该就是为了兼容采购的第三方企业应用。 2C 的话,自定义协议更方便也更符合自己公司的需求 |
9
nobject OP |
10
nobject OP @xiaomada 我们的目标是所有的都会接入到这个账号体系中,包括 gitlab 之后也会接入,但首先是要满足我们整个云平台下的各自子系统的登录,所以最后选择了比较统一的 oidc 协议。
|
11
nobject OP @xiaomada 谢谢,不同域名下的实现方式对我很有帮助。对于单点登录与登出,oidc 的 core 协议里确实没有,但有几个补充协议,好像通过前端通道实现的方式和你下面说的不在同一个域名下的实现方式很类似,就是需要 iframe 去做辅助同步通知到其他域。
基于 oidc 协议的,最后返回的 id_token ,我在想,如果使用传统的 cookie + session 的方式,那他协议返回的 id_token 到底怎么使用呢?看了一些开源的基于 oidc 协议作身份认证的产品,在应用的实现范例大多数是怎么通过授权码去获取到 id_token 这些,就点到为止了 |
12
8rmEHZ8WhVHVOb0E 2023-10-17 14:11:18 +08:00
@nobject access_token 用于授权,因为 oidc 是基于 oauth2 的,而 oauth2 协议原本就有 access_token ,这个用于你更多的功能授权,比如你的 sso 里有一个余额,你在 scope 里授权了余额相关的功能,那么你的 access_token 就可以操作你 sso 提供的余额相关接口,access_token 的格式既可以是 jwt 格式,也可以是自己生成的随机字符串。
id_token 固定为 jwt 格式,里面可以解析出基础用户信息。 针对你问题里的 id_token 怎么使用这个问题,基本与 cas 协议类似, 流程大概如下: 假设现在有商城 shop.qq.com ,后台需要接入员工 sso 1 、员工访问 shop.qq.com/admin ,未登录重定向至 shop.qq.com/admin/login 2 、shop.qq.com/admin/login 判断未登录,重定向至 sso.qq.com/oidc/login?一堆参数 3 、sso 登录后重定向回 shop.qq.com/admin/login?code=xxxxxxxx 4 、shop.qq.com/admin/login 页面通过 code 交换到 access_token 和 id_token 5 、商城系统解析 id_token (或者使用 access_token 去 sso 的用户信息 api 交换),获得用户信息 6 、商城系统从用户信息读取唯一标识,比如 user_id, 判断本系统 user 表是否存在(如果不存在写入 user 表),,然后写入登录信息 这个流程其实 cas 协议没有太大的区别,只是参数和接口发生的了变化。 和 oauth2 的区别就是用户端省略了点击确定授权,然后读取用户信息可以省略掉从 op 读取,而可以直接从 id_token 解析,除此之外基本就和你接入 QQ 登录一样没有区别 |
13
FuryBean 2023-10-17 14:16:31 +08:00
@nobject #9 OIDC 只是一套协议,目前还有一套流行的协议叫 SAML2.0 。支持这两种协议的 SSO 服务,第三方接入会非常方便,因为有很多开源的 SDK 。
|
14
nobject OP @xiaomada id_token 按你的说法,只用于解析出来 userId ,如果子系统 user 表不存在,则写入到 user 表,然后子系统自己想怎么处理系统会话,它自行处理,其实这时候已经与 id_token 就无关了? 还有 id_token 与 access_token 的区别在于本身包含了用户的一些信息,但 access_token 如果需要获取用户信息的话,还需要通过 userinfo endpoint 去获取一次,如果本身不介意再去获取一次的话,其实可能就不需要 id_token 了?我这样理解对么?
|
15
8rmEHZ8WhVHVOb0E 2023-10-17 16:00:09 +08:00
@nobject 第一个问题,userId 是唯一标识,你可能还需要用户名、email 之类的,要啥你 jwt 里带啥就行了。第二个问题,没错,因为 oidc 本来就是在 oauth 里多返回了个 id_token 而已,其他没啥区别。
|
16
8rmEHZ8WhVHVOb0E 2023-10-17 16:02:57 +08:00
@nobject 你可以安装一个 maxkey 调试一下,或者 IDaas 厂商,authing 、阿里云 IDaas 、华为 OneAccess ,都可以免费试用,你接入一个应用调试一遍就好理解了,使用一下他们现成的产品也可以帮助你更好的构思自己的项目。华为的 OneAccess 比较简单好理解
|
17
8rmEHZ8WhVHVOb0E 2023-10-17 16:06:57 +08:00
@nobject 除了 id_token 读取信息之外,他们一般还会有一个同步器和连接器,不同系统叫法不一样,同步器就是从你现有的用户系统读取用户,连接器就是你的用户信息发生了变动,比如密码、email ,会同步到应用系统,一般是 sso 后端通知应用后端,否则你的 sso 里修改了 email ,其他应用要再次授权获取 token 时才能知道 email 修改了,用了连接器的话就可以直接同步过去了。
|
19
Casbin 326 天前
OIDC 和 SSO 本身没有关系,OIDC 是一个认证协议,SSO 是指实现“单点登录”这个效果,也就是一个地方登录了,其他地方也自动登录,实现方式可以是不同的。这两者互相没关系,但是可以在同一个 IAM 系统中实现,具体可以看下 Casdoor 的实现:
Casdoor OIDC: https://casdoor.org/docs/how-to-connect/oidc-client/ Casdoor SSO: https://casdoor.org/docs/how-to-connect/single-sign-on/ |