懒猫微服进阶心得(十四):接入 Casdoor,玩转OpenID Connect(OIDC)
在之前的文章中,我们演示了如何基于 懒猫自带的 OpenID Connect(OIDC) 来实现身份认证。那属于「平台内置」的简化方案,主要是帮助大家快速理解 OIDC 的基本使用场景。
这一次,我们换一个更通用、更贴近生产实践的方式:使用应用商店里的 OpenID Connect(OIDC) Provider —— Casdoor。Casdoor 是一个开源的统一身份认证平台,支持完整的 OIDC 协议,可以作为独立的 IdP(Identity Provider)对接到任何应用。通过它,我们不仅能跑通最标准的授权码流程,还能深入理解 OIDC 的关键环节:授权跳转、Token 换取、ID Token 验签以及用户信息获取。

我们经常听到 单点登录(SSO)、OAuth2、JWT 这些词。
OIDC(OpenID Connect)正是基于 OAuth2 的标准化身份认证协议。它的核心作用是:
- 帮助应用确认用户是谁(认证)
- 不需要你自己维护密码和用户库(交给 IdP)
- 与 OAuth2 完全兼容,可以同时获取访问 API 的能力(授权)
一个形象的比喻:
- OAuth2 提供的是“门禁卡”功能(你能不能进某个房间)
- OIDC 在此基础上加了“身份证”功能(你是谁)
OIDC 基本流程
OIDC 的标准授权码流程(Authorization Code Flow):
- 用户访问应用 → 应用把用户跳转到 IdP 登录页
- 用户在 IdP 登录 → IdP 返回一个授权码(code)
- 应用后端用授权码换 token(包括 access_token 和 id_token)
- 应用验证 id_token → 确认用户身份
- 可选:调用 userinfo 接口 获取更详细的用户信息
进入管理后台
- 登录到你的 Casdoor 管理控制台(通常是 https://casdoor.
.heiyu.space/ 或者部署时设定的管理地址)。 - 用管理员账号(admin/123)进入后台。
- OIDC 应用必须挂在某个 Organization 下。
- 默认有一个
built-in组织,可以直接用,也可以新建一个。

创建应用 (Application)
在左侧菜单选择 Application → 点击 Add。
填写基本信息:
- Name:应用名称(例如
my-oidc-app)。 - Display name:显示名称。
- Organization:选择上一步的组织。
- Logo:可选。

- Name:应用名称(例如
在 Authentication 部分:
设置 Redirect URIs:
必须和你应用里写的一致,例如:
1
http://localhost:5001/auth/callback

在 OAuth 授权类型 部分:勾选
authorization_code(最标准的流程)。
获取 OIDC 参数
保存后,在应用详情页可以看到:
- Client ID
- Client Secret
- Redirect URI(你填的)

同时,Casdoor 服务提供一个 OIDC Discovery 地址:
1 | https://<casdoor-server-domain>/.well-known/openid-configuration |
这个地址返回 JSON,里面包括:
issuerauthorization_endpointtoken_endpointuserinfo_endpointjwks_uriend_session_endpoint
这些就是在后端应用里要配置的参数。
验证配置
在浏览器里直接访问:
1
https://<casdoor-server-domain>/.well-known/openid-configuration
如果能看到 JSON,说明 OIDC 服务已开启。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69{
"issuer": "https://casdoor.xxxx.heiyu.space",
"authorization_endpoint": "https://casdoor.xxxx.heiyu.space/login/oauth/authorize",
"token_endpoint": "https://casdoor.xxxx.heiyu.space/api/login/oauth/access_token",
"userinfo_endpoint": "https://casdoor.xxxx.heiyu.space/api/userinfo",
"jwks_uri": "https://casdoor.xxx.heiyu.space/.well-known/jwks",
"introspection_endpoint": "https://casdoor.xxx.heiyu.space/api/login/oauth/introspect",
"response_types_supported": [
"code",
"token",
"id_token",
"code token",
"code id_token",
"token id_token",
"code token id_token",
"none"
],
"response_modes_supported": ["query", "fragment", "login", "code", "link"],
"grant_types_supported": ["password", "authorization_code"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": [
"RS256",
"RS512",
"ES256",
"ES384",
"ES512"
],
"scopes_supported": [
"openid",
"email",
"profile",
"address",
"phone",
"offline_access"
],
"claims_supported": [
"iss",
"ver",
"sub",
"aud",
"iat",
"exp",
"id",
"type",
"displayName",
"avatar",
"permanentAvatar",
"email",
"phone",
"location",
"affiliation",
"title",
"homepage",
"bio",
"tag",
"region",
"language",
"score",
"ranking",
"isOnline",
"isAdmin",
"isForbidden",
"signupApplication",
"ldap"
],
"request_parameter_supported": true,
"request_object_signing_alg_values_supported": ["HS256", "HS384", "HS512"],
"end_session_endpoint": "https://casdoor.xxxxx.heiyu.space/api/logout"
}用 Postman 或者 oidc-client 测试一下授权流程,看看能不能拿到
code、access_token、id_token。
✅ 至此,Casdoor 端就配置好了。剩下的就是在应用端(RP)写 OIDC 客户端代码
OpenID Connect(OIDC)代码
1 | import os, requests, jwt |
这个是.env 的环境变量:
1 | FLASK_SECRET_KEY=replace-with-a-random-32-bytes-string |
下面是代码解读:
Flask 应用 = OIDC 客户端(RP);Casdoor = 身份提供方(IdP)。
/login:把浏览器重定向到 IdP 的 授权端点。/callback:IdP 回调携带code→ 后端用code去 令牌端点 换access_token+id_token→ 用 JWKS 公钥校验id_token→ 用access_token拉 userinfo。/profile:展示从 userinfo/ID Token 得到的用户信息。/logout:清空本地会话。
依赖与配置
1 | import os, requests, jwt |
requests:调 OIDC 的 HTTP 端点。PyJWT+cryptography:验证id_token的数字签名。PyJWKClient:根据 JWT 头里的kid,自动从jwks_uri拉对应公钥。urlencode:把授权请求的参数拼到 URL 上(避免手写字符串拼接出错)。
1 | load_dotenv() |
- 从
.env读取 Issuer / Client / Secret / Redirect URI,和 Casdoor 后台登记的必须完全一致(协议、域名、端口、路径一字不差)。
通过 Discovery 自动找端点
1 | discovery = requests.get(f"{ISSUER}/.well-known/openid-configuration").json() |
- OIDC 规范要求 IdP 暴露发现文档,里面告诉你:授权端点、令牌端点、用户信息端点、JWKS 公钥地址等。
- 这么做的好处:不写死 URL,换 IdP/升级版本也不怕路径差异。
首页 & 登录
1 |
|
- 简单展示:有会话就显示用户名,否则给一个“登录”链接。


1 |
|
- state:浏览器去 IdP 再回来时要原样带回;用于确认这真是你发起的请求(防 CSRF)。
- nonce:IdP 会把它放进
id_token,回调后核对一致(防重放/混淆响应)。
Demo 为了短小,先写了固定值。写文章时要强调:生产必须随机、并在回调里校验。
回调:换令牌 → 验签 ID Token → 拉用户信息
1 |
|
- IdP 会带着
?code=...&state=...回来。这里先取出code,并处理错误场景(用户取消授权等)。
1 | # 1) 用 code 换 token |
- 这一步是服务端到 IdP的 POST。两种常见做法:
- 把
client_id/client_secret放表单(本例); - 或用 HTTP Basic(IdP 要求不同,文章里可顺带提一嘴)。
- 把
- 返回里关键是
id_token(JWT,证明“你是谁”)和access_token(调用userinfo的 Bearer Token)。
1 | # 2) 验证 id_token(必须做) |
- 为什么一定要验签?否则任何人都可以伪造一个 “自称由 IdP 签发”的 JWT。
PyJWKClient会读 JWT 头部的kid,去JWKS_URI拉这把钥匙的公钥。- 同时校验标准字段:
iss/aud/exp/iat/...。如果你还用了nonce,建议对claims["nonce"]做一致性校验。
1 | # 3) 拉取用户信息 |
- 不是所有 IdP 都在
id_token里带全资料,所以通常再拉一次userinfo。 - 这一步需要前面的
access_token。
1 | # 4) 缩小会话,只存最小字段(避免 Cookie > 4KB) |
- 强烈建议:只把少量字段放进 session(Flask 默认把 session 放加密 Cookie,4KB 有上限)。不要把整个 token/JWT/raw 塞进去。
查看资料 & 退出
1 |
|
- 只从会话里取“精简后的用户档案”返回给前端。

1 |
|
清本地会话即可。若要单点登出,可以再调用 IdP 的
end_session_endpoint(用 Discovery 取到)并带post_logout_redirect_uri。
小结:懒猫微服 + Casdoor OIDC
通过这次实战,我们完成了一个从 Casdoor 配置 到 应用端代码实现 的 OIDC 流程。
这说明在懒猫微服中,大家不仅可以直接用内置的 OIDC 功能,还可以自由选择商店里的 OIDC Provider(如 Casdoor) 来扩展身份认证能力。
懒猫微服不仅能用自带的 OIDC,更能灵活调用商店里的 Casdoor 等 Provider,满足更灵活的认证与单点登录需求。
懒猫微服进阶心得(十四):接入 Casdoor,玩转OpenID Connect(OIDC)


