OIDC(OpenID Connect)的标准已经完成许久,然而普及的程度一直很低。 我觉得主要的普及障碍之一就是缺少足够的科普教程,最佳实践。 如此复杂的协议,如果没有一个直观的介绍,容易让人失去兴趣。 这个小文章就是想用一个杜撰的故事来给读者一个对于OAuth2和OIDC的直观体验。 但愿有所帮助。

—–

从前,小A做了一个网站A,用户可以在网站上买菜,用户很开心,小明也很开心。 后来,小B做了一个网站B,目标是整合用户在所有买菜的网站上的购物记录。 于是小B找到小A说:能不能把让我获取你的全部用户购买记录呀?

小A说:做梦吧!又不是所有用户都同意呢!

小B说:那要不,我在要访问一个你的用户的购买记录前,我把用户重定向到你的网站,然后你可以问问用户愿不愿意分享这数据。假如同意,你就给我发个你签过名的token如何?

小A:这个可以,那我就开始写啦。

小B:且慢且慢,有这么个协议叫OAuth2哦,大家都用这个来搞定应用间授权(authorization)哦。用了这个协议,要是下一次比如小C想用你的数据,你就不用单独写一套给小C用的代码啦。一旦你实现了这个OAuth2之后,你可以准备一个“开发者”页面,然后别的开发者就可以直接注册。OAuth2的协议里,把除你以外的网站都叫做client哦。

小A:这么棒,那到底咋工作?

小B:很简单啊,你批准了我(也就是client)的注册请求后,按照协议,你要给我颁发client_idclient_credentials哦。每次我想访问你的数据,比如用户在我的网站上点击“连接A网站”,我就把用户带到你的一个授权页面,这个授权页面上会写说类似于“B站想要访问你的X/Y/Z资源,同意否?“信息。

小A:照这么说,我还是要单独为你写一个网页咯?

小B:不用的啦,按照协议,我带用户来你那儿的时会顺便带来我的client_id并且说清我想访问啥数据,并且告诉你如果用户同意之后你要把用户带去哪,所以你只要写一个授权页然后根据client_id和其他数据来显示内容就好啦,以后别的client也用这个数据✌️。

小A:好吧好吧,那在这个授权页,总之用户要是说同意,我就把用户带回你那?

小B:对的,你把用户带回我那儿时,要顺便给我个凭证哦。我可以拿着这个凭证和我的client_credential去你那儿领取最终的权限凭证,按照协议,也就是传说中的access_token啦。咩哈哈哈😂

小A:有了这个你就可以为所欲为?

小B:没没没不用担心😁,我只能做我被scope授权做的事。而且access_token会过期的。最不济,我拿着access_token访问你的服务的时候,你可以再检查一下嘛。

小A:这个好啊!做!

—–

注解:以上的讲解是个简化版OAuth2的Authorization Code Flow,也就是针对传统的服务端渲染网站的client所做的安排,如果client是App之类的,还会需要别的安排。

—–

时光荏苒。小A变强了,也变秃了。小A的网站发展成了横跨生活诸多领域业界巨头,几乎现在人手都有一个小A的账号。 小B还是个小公司。

小B又找到小A:诶,近来用户都不愿意注册登陆了,因为他们抱怨注册了太多服务,反感了。

小A:嘿!我这儿有个好方法哦。记得几年前你跟我提的专门做授权(authorization)的OAuth2协议吗? 现在大家基于这个协议,做了一个叫OIDC(OpenID Connect)的专门做认证(authentication)的协议哦! 我们已经支持了这个协议,所以你可以试试我们最新的“用A登陆”的功能哦,放了这个在你的登陆页上,不想注册的用户就可以直接用A站账号登陆B站啦。

小B:这么厉害!的确如此,仔细一想,认证是授权的子集。难不成,就是在OAuth2的scope中添加一个用户信息然后直接用access_token来当我网站登陆的token,就完事了?

小A:聪慧!这的确是OIDC的环节(scope中包含oidc)。但不完整,OAuth2毕竟不是完全的认证协议。你想,如果用access_token来做登陆凭证,这就意味着你每次想确认一个用户是否登陆,都要拿着access_token到我那儿来做个检查。这可不行呀,增加了你的延迟,也增加了我的负担。

小B:那假如,让access_token中包含用户信息就好啦?只要access_token不过期,用户可以被视为登陆状态,还不用去你的服务器请求用户信息。

小A:这想法可以,OIDC也的确就是这么回事。它修改了OAuth2,让我的服务器返回access_token的同时还返回一个用户凭证(id_token),此凭证包含了主要用户信息。这个凭证是用密钥认证过的,也就是说可以轻松判断是不是A站颁发的,而且和access_token一样,都会过期。这个id_token就可以被用来当你的内部用户token的。

小B:嗯。听起来不错。那总结一下,我去你那儿注册了OIDC后,我就可以在首页上安排一个“用A登陆”的按钮。用户点了这个按钮会被带到你的授权页,用户在这个授权页登陆,授权。然后你把用户带回我的网站并且带着凭证。我拿着凭证和我的client_credentials交换id_token。我把id_token存在cookie,session之类的地方。用户就完成了用A账号登陆的流程。

小A:正是。但你知道这协议威力不只如此,我若是愿意,还可以成为单点登录提供商(SSO Provider)。 也就是说,用户登陆我的平台,他可以看到所有他可以登陆的服务,他只要点击任意服务,比如B站,我就能带他去B站。 这样他就连B站的登陆页面都不用看到。这样用户想去哪,只要通过A站就可以完成,岂不美哉?我岂不是是能赚…….

小B粗暴的打断了小A:土豪坐下!

—–

注解:以上的讲解是简化版的OIDC,但包含了协议核心。 虽然如此,在安全类协议中细节是很重要的。阅读原版的协议文档是很重要的。

但愿这白话足够白