飞布支持OIDC进行身份验证,实现了OIDC中定义的两种授权流程:基于cookie登录-授权码模式(Authorization Code)和基于Token登录-隐式模式(Implicit)。
支持OIDC Provider
飞布能与任意实现OIDC规范的供应商集成。目前主流OIDC供应商如下:
快速操作
基本设置
首先,输入供应商名称,自动生成 “登录回调 URL”。
然后,前往AUTHING应用配置页,获取APP ID 、App Secret和Issuer ,分别填入身份验证器表单。
接着,输入Issuer。输入后,系统自动生成服务发现地址,并从中获取jwksURL和用户端点
当开发WEB应用时,开启基于cookie的模式,同时填入App Secret。
若JwksURL无法访问,可将JWKS切换到JSON模式,然后输入JSON字符串。
{
"kty": "RSA",
"e": "AQAB",
"n": "1GBBv-QOtbNIvgJZqvW2nvIrNx6-YNKJAD3L3WspAcx1y-RYctI2RBb4k4GN0du8AH2UUf8wBywONHplYAw1djkWAztHgj4cc_WxqKvD1t5bNNjRW7I5EPA9ZEkFblIAxZVwhOPK5H8KLgiVaD7y9fPEks6sVhu2VUQKC0Qr85-0WJVzmXP3QH_1yLn1qRpkJtjCW1I4DPsB0TrQC6WBMy99Io8zECraueLrJFApuRx1H_MwgDwnt4VlYuaoqU17TyBUQWO077mUB-FFI-s0jALuPAUuNWHFFogTq2cbydaSfPcWQPjylYcLcIt-bBBdedLqsTk_0nTXPqREMFwexw",
"alg": "RS256",
"use": "sig",
"kid": "AUQR2TFiVexgvm0j0PrbZ3ofEz9R2eG7qvEJP9Ua2f0"
}
值得注意的是,基于Cookie的登录,需要OIDC供应商(OIDC Provider)和飞布服务器后端(Relying-Party)同时配置回调地址。
首先是,复制 登录回调 URL, 前往AUTHING设置“登录回调 URL”,多个URL可用"英文逗号"分开。
接着,点击“配置登录回调”按钮,前往"设置->安全",设置 "重定向URL"。
系统提供了两个默认值:
localhost:9123/#/workbench/userInfo:用户详情页回调URL,用于测试OIDC
localhost:9123/#/workbench/rapi/loginBack:API预览页回调URL,用于测试需要授权的API接口。
后续,可根据集成的前端项目,添加对应URL。
回到详情页,点击右上角“测试”按钮。跳转至authing提供的登录页,登录后,可查看当前用户信息。
只有开启“基于Cookie”模式后,才能直接测试。
出于安全考虑,在回调至OIDC供应商URL时,系统会自动跳转到HTTPS链接。若想关闭该功能,可关闭 “强制 HTTPS 跳转”。
API设置
前往API管理面板,选择需要设置的API
登录访问:切换到设置面板,开启授权,限制API必须登录才能访问
数据权限:用@fromClaim修饰入参,限制API的数据权限
输入参数,测试接口,你会发现,接口返回401(这是因为你没登录)
在预览页顶部,选择OIDC供应商,点击前往登录,登录后可查看用户信息
重复步骤3,可以看到接口执行成功,有三种情形
登录访问:未限制数据权限,正常执行,唯一区别是需要用户登录才能执行
变更请求:限制数据权限,插入数据时绑定当前用户的标识,如UID或EMAIL等
客户端如何使用
基于COOKIE登录
构建如下URL,在网页上点击跳转即可。
http://localhost:9991/api/auth/cookie/authorize/<供应商ID>?redirect_uri=<当前页URL>
当前页URL:对应"设置->安全"中的"重定向URL"
基于TOKEN登录
客户端需要向请求中添加以下请求头:
Authorization: Bearer <token>
如何获取TOKEN,可参考Authing的SDK。
工作原理
专业术语
RP(Relying-Party)
:服务器后端,这里指飞布服务器后端
OP(OIDC Provider)
: 提供身份验证的服务器,例如Authing 服务器
基于cookie登录-授权码模式
授权码模式是 OIDC 授权登录中最常用的模式,OP 服务器返回一个授权码 code 给开发者后端服务器,在后端完成 code 换取 access_token,再用 access_token 换取用户信息的操作,从而实现用户的身份认证。
1. 发起登录请求
发起授权需要拼接一个用来授权的 URL,并让终端用户在浏览器中访问,具体参数如下:
<Issuer>/auth
?response_type=code
# RP身份标识,对应App ID
&client_id=29352915982374239857
# 授权服务器接收请求后返回给浏览器的跳转访问地址,对应 设置->安全->重定向URL
&redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback
&scope=create+delete
&state=xcoiv98y2kd22vusuye3kch
Issuer:OP服务的连接,例如:https://<应用域名>.authing.cn/oidc/
2.用户登录
发起 OIDC 登录之后,如果用户先前未在 OP 登录过,OP 会将用户重定向到登录页面,引导用户完成在 OP 的认证,此时用户需要选择一种方式进行登录:
3.获取code
OP将验证此用户是否合法,验证通过后会将浏览器重定向到发起授权登录请求时指定的 redirect_uri 并通过 URL query 传递授权码 code 参数。
https://example-client.com/redirect
?code=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3 # 授权码,用于换取access_token,用一次就失效
&state=xcoiv98y2kd22vusuye3
4.使用code换取token
飞布默认换取 token 身份验证方式为 client_secret_post,需要向token_endpoint
发送POST请求,具体如下:
# POST <token_endpoint> //
{
code:"g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3" # 授权码
client_id: "29352915982374239857", # RP身份标识,对应App ID
client_secret: "xxxx", # RP密钥,对应App Secret。
grant_type: "authorization_code",# 指定RP正在使用的授权流程。
# 与请求authorization code时使用的redirect_uri相同。
redirect_uri:"https%3A%2F%2Fexample-client.com%2Fcallback ",
}
token_endpoint一般从服务发现地址中获取,格式:<
Issuer>/.well-known/openid-configuration
5.签发访问令牌
OP将会验证第4步中的请求参数,当验证通过后(校验authorization code
是否过期,client id
和client secret
是否匹配等),OP将向RP
返回access token
。
{
# 访问令牌
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJqdGkiOiJQZU41YXg1b3FabGRhcUJUMzQzeUkiLCJzdWIiOiI1Y2U1M2FlYTlmODUyNTdkZDEzMmQ3NDkiLCJpc3MiOiJodHRwczovL29hdXRoLmF1dGhpbmcuY24vb2F1dGgvb2lkYyIsImlhdCI6MTU4MTQyMDk1NywiZXhwIjoxNTgxNDI0NTU0LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGF1dGhpbmdfdG9rZW4gZW1haWwgcGhvbmUgYWRkcmVzcyBvZmZsaW5lX2FjY2VzcyIsImF1ZCI6IjVkMDFlMzg5OTg1ZjgxYzZjMWRkMzFkZSJ9.rtpRSL3_U03zXShZUCILquSR_KEDuS-OldWpy8RLztWUNG_tMyrg2g9CG4hC7pJUwmgzZKtp7vsVrj6W0eyo_ehE4KGz9iKnyd46DFbx9W9pi-mieRW5HuVMGL2zvDH8zF467WXET2SVB3LUhFLNmEbxpvjPZ5Ksvbcd7nqHfnUN4-z3SqAvhGWWfcmt7QDFlLtWPw4LzyznEqmM9sDkNiNDnTkjmcjm7yHJR-yv5FvpzQB2kraQVOrrdAixbHf29ihOVO25CrjmgeKemg1vuLNGUcOrr_XWn7xaCSvyAfXrBuRalecW9RA4p_Cp6YslHc_572awekt3kUO2TebUQA",
"expires_in": 3597,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJzdWIiOiI1Y2U1M2FlYTlmODUyNTdkZDEzMmQ3NDkiLCJiaXJ0aGRhdGUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImdlbmRlciI6IiIsImdpdmVuX25hbWUiOiIiLCJsb2NhbGUiOiIiLCJtaWRkbGVfbmFtZSI6IiIsIm5hbWUiOiIiLCJuaWNrbmFtZSI6IiIsInBpY3R1cmUiOiJodHRwczovL3VzZXJjb250ZW50cy5hdXRoaW5nLmNuL2F1dGhpbmctYXZhdGFyLnBuZyIsInByZWZlcnJlZF91c2VybmFtZSI6IiIsInByb2ZpbGUiOiIiLCJ1cGRhdGVkX2F0IjoiIiwid2Vic2l0ZSI6IiIsInpvbmVpbmZvIjoiIiwiY29tcGFueSI6IiIsImJyb3dzZXIiOiIiLCJsb2dpbnNfY291bnQiOjEwMywicmVnaXN0ZXJfbWV0aG9kIjoiZGVmYXVsdDp1c2VybmFtZS1wYXNzd29yZCIsImJsb2NrZWQiOmZhbHNlLCJsYXN0X2lwIjoiMTIxLjIxLjU2LjE3MSIsInJlZ2lzdGVyX2luX3VzZXJwb29sIjoiNWM5NTkwNTU3OGZjZTUwMDAxNjZmODUzIiwibGFzdF9sb2dpbiI6IjIwMjAtMDItMTFUMTE6MzU6MTUuNjk2WiIsInNpZ25lZF91cCI6IjIwMTktMDUtMjJUMTI6MDQ6NTguMjk0WiIsInRva2VuIjoiZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmtZWFJoSWpwN0ltVnRZV2xzSWpvaWRHVnpkRE5BTVRJekxtTnZiU0lzSW1sa0lqb2lOV05sTlROaFpXRTVaamcxTWpVM1pHUXhNekprTnpRNUlpd2lZMnhwWlc1MFNXUWlPaUkxWXprMU9UQTFOVGM0Wm1ObE5UQXdNREUyTm1ZNE5UTWlmU3dpYVdGMElqb3hOVGd4TkRJd09URTFMQ0psZUhBaU9qRTFPREkzTVRZNU1URjkuM0l0X0NJQTNFbUpoYWcyMW92WjNwd0RfY0owcTVTZkJjSURSZThRX3FoayIsImVtYWlsIjoidGVzdDNAMTIzLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicGhvbmVfbnVtYmVyIjoiMTMxMTIzNDEyMzQiLCJhZGRyZXNzIjoiIiwiYXRfaGFzaCI6IjV6QnNUOHF4RHc1RmNYdU55UFg4YUEiLCJzaWQiOiJkNmZiOTE5Ny00NmE3LTQ1ZGEtOGVkMC05ODhjZjg0ZjQwZWUiLCJhdWQiOiI1ZDAxZTM4OTk4NWY4MWM2YzFkZDMxZGUiLCJleHAiOjE1ODE0MjQ1NTQsImlhdCI6MTU4MTQyMDk1NywiaXNzIjoiaHR0cHM6Ly9vYXV0aC5hdXRoaW5nLmNuL29hdXRoL29pZGMifQ.VZzqULytIteyBfouww5TsHQ50gEhM06kUWMeDiO3FVFSCW9ys2bFPos5p6LFzliK4Ce09ypOwVQiRnE2gNYsukLvlUPlKDIP_Xk5W19frKi1Z8ImuIPvUqVMKbFutVNS0TfIPCPJVBl8C1j5OXeIs6z0V90QrvyJao6FqVEa3axOHxbhpo1fH2hP04-wkGOp_l10d7RFhGcnPyPnz9-C5X6A4UEsCSDCVw1mDQHxDSFP9OPaB_OlCG_Bi6G-CeLhPa3V5hyIefdBvxC9SIpK-6qY-_BfsNKkBHDVKMb0xodgN2hzn3UTUGBuuoiaB4JhCv72EZ7eiXKIXFz6zVcogA",
"refresh_token": "DuSPlrUFPAvCZ1WQKarv5MbEsXN",
"scope": "openid profile authing_token email phone address offline_access",
"token_type": "Bearer"
}
基于Token登录-隐式模式
OIDC 隐式模式不会返回授权码 code,而是直接将 access_token
和 id_token
通过 URL hash 发送到回调地址前端,后端无法获取到这里返回的值,因为 URL hash 不会被直接发送到后端。该模式常结合移动应用或 Web App 使用。
1. 用户授权请求
发起隐式模式的授权登录需要拼接一个 URL,并让终端用户在浏览器中访问,不能直接输入认证地址域名。具体参数如下:
<authorization_endpoint>
# RP身份标识,对应App ID
?client_id=0oabv6kx4qq6h1U5l0h7
&response_type=token # 为token 或 id_token
# 回调链接,用户在 OP 认证成功后,OP 会将 id_token、access_token 以 URL hash 的形式发送到这个地址。
&redirect_uri=http%3A%2F%2Flocalhost%3A8080
&state=state-296bc9a0-a2a2-4a57-be1a-d0e2fd9bb601
&nonce=foo
authorization_endpoint一般从服务发现地址中获取,常见格式:<
Issuer>/
authorize
服务发现地址:<
Issuer>/.well-known/openid-configuration
2.用户授权应用(略)
3.用访问令牌重定向URI
假设用户授予访问权限,跳转后链接示例:
http://localhost:8080/
# 访问令牌, 以 URL hash 形式传递
#access_token=eyJhb[...]erw
# 当且仅当response_type设置为 token 时返回,值恒为 Bearer
&token_type=Bearer
&expires_in=3600
&scope=openid
&state=state-296bc9a0-a2a2-4a57-be1a-d0e2fd9bb601
为什么信息在 URL hash 里而不是 query 里?因为 hash 内容不会直接发送到服务器,避免 id_token、access_token 被盗用。
1-3步骤为标准流程,不同客户端获取access_token 的流程不同,需要根据实际情况处理。从工程实践中看,常用的另一种方式是直接调用OIDC供应商的登录接口,从中获取access_token。
4.传递给应用程序的访问令牌
浏览器向RP发送access token
。RP采用两种方式校验令牌:
公钥签名校验:优先使用使用公钥验证签名。公钥地址(jwks_uri)一般为: <Issuer>/.well-known/jwks.json
。
**在线接口校验:**若公钥验签失败,则调用供应商的token验证接口进行在线验证。userinfo_endpoint?
获取用户信息
直到access token
过期或失效之前,RP
使用access_token,通过OP的userinfo_endpoint
API换取用户信息。如果发起授权登录时的 scope 参数不同,这里的返回信息也会不同,返回信息中的字段取决于 scope 参数。字段符合 OIDC 规范 (opens new window),用户信息字段与 scope 对应关系请参考 scope 参数对应的用户信息。
具体请求如下:
GET <userinfo_endpoint>?access_token=<access_token>
userinfo_endpoint一般从服务发现地址中获取,常见格式:<Issuer>/userinfo
返回值示例:
{
"sub": "5f7174df27e0eb9c6d21436d",
"birthdate": null,
"family_name": null,
"gender": "U",
"given_name": null,
"locale": null,
"middle_name": null,
"name": null,
"nickname": null,
"picture": "https://usercontents.auth0.cn/avatar.png",
"preferred_username": null,
"profile": null,
"updated_at": "2020-09-28T05:33:15.892Z",
"website": null,
"zoneinfo": null
}
OIDC规范中不包含角色的描述,因此返回值不涉及roles
字段