白话OAuth2,OIDC和SSO
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_id
和client_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,但包含了协议核心。 虽然如此,在安全类协议中细节是很重要的。阅读原版的协议文档是很重要的。
但愿这白话足够白