文章目录
提到OAuth2,大家多少都有些了解。
不了解的话可以先看下之前的简单聊聊鉴权背后的那些技术先回顾一下基本概念和流程。
简单来说,以google授权为例,一般就是通过用户授权页面登录google账号,再跳转用code换取到相应权限的token,就可以代表用户去发起一些google api的请求。
直接代码实现这套授权逻辑并不复杂,不过如果还需要接入facebook授权,instagram授权呢,总不能挨个去实现一遍吧。
最好能有一套通用的解决方案来解放双手, 今天我们就聊聊如何用keycloak实现一套通用的身份验证和授权管理方案。
提前说明,无法本地复刻的技术方案不利于理解,也不利于方案探讨。虽然本文章所用代码是使用了
rust的axum框架(为啥?因为rustis future!)+keycloak,但从服务启动到keycloak服务及相关配置,都用docker-compose+terraform+shell脚本化管理,可100%本地复刻,欢迎本地尝试。(当然我说的是Mac下)代码地址: https://github.com/NewbMiao/axum-koans
初探OAuth
在引入keycloak之前我们以google为例先看下常规OAuth怎么接入,方便后边和keycloak接入对比。
前置工作: 获取
google OAuth application的clientId和clientSecret,不清楚的话,可以参考 Create a Google Application in How to setup Sign in with Google using Keycloak
如下图,一般授权流程(standard flow)中客户端和auth server主要是两个阶段
- 生成
auth url跳转登录后请求换取授权令牌的code - 在
auth callback中用code换取token,得到能代表用户的credentials,一般是accessToken

这个流程自己也可以实现,但一般都用oidc client(其实现了OpenID connect协议,是建立在OAuth2.0上的身份验证协议,用来为应用提供用户身份信息)来实现。
编程语言实现上大同小异,下边代码以rust的oauth2库为例讲解
如果不熟悉rust,可以重点看代码注释,也不影响理解
初始化oidc client
1 | // src/extensions/google_auth.rs@GoogleAuth::new |
生成auth url
1 | // src/extensions/google_auth.rs@GoogleAuth::auth_url |
这里参数access_type=offline对于应用需要长期accessToken是很关键的。一般accessToken都有过期时间,如果没有有效的refreshToken来刷新accessToken,就会有accessToken失效后还要用户再登录的尴尬局面-_-!
另外为安全考虑除了可以用state做请求合法校验,还可以用PKCE(Proof Key for Code Exchange)来加强, 实际用到的代码有实现,感兴趣可以看下
auth callback换取token
1 | // src/extensions/google_auth.rs@GoogleAuth::get_tokens |
这部分不复杂,按文档配好本地,可以访问http://localhost:8000/google/auth来尝试上述flow
使用keycloak IDP
keycloak 配置
上边流程怎么让keycloak这个身份和访问管理系统接管呢,答案是使用keycloak IDP (Identity provider)
我们先看下需要如何配置相应配置,这里先用terraform - keycloak provider 展示下配置。
等效的页面配置可以后边参考之前的链接 How to setup Sign in with Google using Keycloak
1 | # 这里使用默认的admin-cli配置keycloak |
别看代码版的配置稍微有点多,主要配置其实就只有注释里的三处,然后google OAuth的代理设置就完成了,不信我们继续往下看怎么代码接入
keycloak auth接入
上边keycloak配置了realm,后边授权和token获取都会和这个realm下的issueUrl打交道,这里issueUrl就类似google的auth server 地址。
- 初始化
keycloak oidc client
1 | // src/extensions/keycloak_auth.rs@KeycloakAuth::new |
- 生成
auth_url
方法基本和之前google配置一模一样。
这里也能看出为啥需要oidc协议,其实就是抽象化,提供了一种安全、标准化和可扩展的身份验证和授权协议。它简化了应用程序中的身份管理和访问控制,提供了一致的用户登录体验,并提高了应用程序的安全性。
这里auth url默认跳转的是keycloak登录页面,然后google idp是作为一种登录选项让用户选择。但如果就打算让用户直接google登录,可以跳过keycloak登录页。
方法是使用客户端建议的idp(kc_idp_hint):Client-suggested Identity Provider
这样就可以直接使用指定的idp进行授权登录
代码如下
1 | // src/extensions/keycloak_auth.rs@KeycloakAuth::auth_url |
auth callback换取token
方法也同 google auth callback, 这里不赘述了。
不过这里拿到的是keycloak的token。要是需要google的token怎么办?
别急,有两种办法。
方法一: token-exchange
token-exchange 是用于token交换场景,我们这里是用keycloak token换取外部google token(external token)
相应keycloak配置
token-exchange目前还是keycloak预览(preview)功能,需要至少在features中启用admin-fine-grained-authz,token-exchange才可使用(详见keycloak docker-composer配置 )
1 | // 启用idp获取refresh token |
代码实现
1 | let token_url = |
这样就获取到了可用的google access token, 实际上内部是通过google refresh token换取到的。
这样常规请求没问题了,只要你有keycloak access token, 就能换取到google access token来请求google api。so easy?!
方法二:broker 读取 stored token
然而,要是需要google refresh token怎么办?
有些场景是客户端需要自己通过google refresh token换取access token来发起请求的,难道这个时候客户端先去拿个keycloak access token么。。。?
这就可以用Retrieving external IDP tokens
底层实现是授权时存储了external token,再配合添加broker read token权限给生成的用户,就可以用keycloak access token换取存储的external access token + refresh token.
相应keycloak配置
1 | resource "keycloak_oidc_google_identity_provider" "google" { |
题外话:这里
add_read_token_role_on_create对应的配置在21.1.1版keycloak admin页面没有,但admin api确可以设置,也是很tricky
代码实现
就是直接换取refresh_token, 请求地址指明对应的idp即可
1 | // src/extensions/keycloak_auth.rs@KeycloakAuth::get_idp_token |
题外话:当然直接给用户这么获取
refresh token的能力并不安全,还需要考虑对broker read token接口的访问约束等来更好的保证安全token换取。
上边keycloak授权方案可以本地配好环境后,用http://localhost:8000/keycloak/login 来尝试。
好了,keycloak如何管理external auth到这里就结束了。以上是我在使用keycloak的一些摸索和思考,欢迎大家一起探讨。
再次附上本文的代码地址以供验证:https://github.com/NewbMiao/axum-koans
如有疑问,请文末留言交流或邮件:newbvirgil@gmail.com本文链接 : https://newbmiao.github.io/2023/06/18/auth-manage-via-keycloak.html
本文链接 : 