Skip to main content

认证全链路:从 Casdoor JWT 到业务 Principal

1. 总体架构

认证链路分 6 个阶段:

阶段组件产出
① OIDC 登录Casdoor + Envoy GatewayJWT
② JWT 验签Envoy Gateway JWKS验证后的 JWT claims
③ claimToHeadersEnvoy SecurityPolicyx-aisphere-external-* HTTP headers
④ Header 提取Kernel GatewayTrustedExtractorauthn.Credential
⑤ Principal 重建PrincipalFromTrustedHeadersauthn.Principal(21 字段)
⑥ Context 注入withPrincipalauthn.ContextWithPrincipal + contextx.WithAuthnPrincipal

二、Casdoor JWT 结构

2.1 获取 Token

2.2 JWT Payload 示例

{
"owner": "aisphere",
"name": "admin",
"id": "496333c7-7acc-4717-8596-056544fc0a68",
"type": "normal-user",
"displayName": "管理员",
"email": "user@example.com",
"phone": "13800138000",
"iss": "https://casdoor.example.com:30723",
"sub": "496333c7-7acc-4717-8596-056544fc0a68",
"aud": ["869aff97ab0408cbbd1c"],
"exp": 1784106688,
"iat": 1783501888,
"tokenType": "access-token",
"scope": "openid profile email",
"owner": "aisphere",
"roles": [],
"groups": []
}

三、Envoy Gateway claimToHeaders 配置

3.1 配置

claimToHeaders:
# 主身份标识
- claim: sub → x-aisphere-external-sub
- claim: iss → x-aisphere-external-issuer
- claim: email → x-aisphere-external-email
- claim: name → x-aisphere-external-name
- claim: displayName → x-aisphere-external-display-name
- claim: phone → x-aisphere-external-phone
- claim: owner → x-aisphere-external-owner
- claim: id → x-aisphere-external-id
- claim: scope → x-aisphere-external-scope
- claim: azp → x-aisphere-external-authorized-party
# 内部投影
- claim: sub → x-aisphere-principal
- claim: id → x-aisphere-user-id
- claim: owner → x-aisphere-org-id

3.2 完整字段映射表

HTTP HeaderJWT Claim值示例说明
x-aisphere-external-subsub496333c7-...JWT 主体(用户 UUID)
x-aisphere-external-issuerisshttps://casdoor.example.com:30723JWT 签发者
x-aisphere-external-emailemailuser@example.com邮箱
x-aisphere-external-email-verifiedemail_verifiedfalse邮箱是否已验证
x-aisphere-external-namenameadmin用户名
x-aisphere-external-display-namedisplayName管理员显示名称
x-aisphere-external-phonephone13800138000手机号
x-aisphere-external-ownerowneraisphereCasdoor 组织
x-aisphere-external-idid496333c7-...Casdoor 用户 ID
x-aisphere-external-typetypenormal-user用户类型
x-aisphere-external-scopescopeopenid profile emailOAuth2 scope
x-aisphere-external-authorized-partyazp869affc-...Client ID
x-aisphere-principalsub496333c7-...内部投影:Principal ID
x-aisphere-user-idid496333c7-...内部投影:用户 UUID
x-aisphere-org-idowneraisphere内部投影:组织 ID

四、Kernel 框架处理链路

4.1 完整处理流程

4.2 健壮性设计

场景行为是否报错
Header 不存在headerValue() 返回空字符串
Header 值为空对应 Principal 字段为空
Scopes 格式多样splitCSV() 支持逗号/分号/空格
SubjectID 完全缺失principalFromGatewayClaimHeaders 返回 false✅ 拒绝请求
Internal token 不匹配TrustedHeaderAuthenticator 返回错误✅ 拒绝

4.3 SubjectID 优先级

1. x-aisphere-principal(GatewayClaimHeaderPrincipal)
2. x-aisphere-user-id(GatewayClaimHeaderUserID)
3. x-aisphere-external-id(GatewayClaimHeaderExternalID)
4. x-aisphere-external-sub(GatewayClaimHeaderExternalSub)

4.4 OrgID/TenantID 优先级

1. x-aisphere-org-id(GatewayClaimHeaderOrgID)
2. x-aisphere-external-owner(GatewayClaimHeaderExternalOwner)
3. x-aisphere-owner(TrustedHeaderOwner)

五、PR #27:contextx.Principal 无损镜像

5.1 修复前 vs 修复后

5.2 字段对比

字段修复前修复后
SubjectID
SubjectType
Provider
ExternalID
Issuer
Audience
TenantID
OrgID
AppID
ProjectID
Username
Name
Email
Phone
Roles
Groups
Scopes
AuthMethod
Attributes
IssuedAt
ExpiresAt

六、IAM 后端 Principal 使用审计

6.1 认证模式

IAM 使用 gateway_trusted 模式,配置在 internal/server/access.go:37

if strings.EqualFold(cfg.Authn.Mode, securityx.AuthnModeGatewayTrusted) {
internalCall.Enabled = false
internalCall.Token = ""
}

6.2 完整认证链路

6.3 Principal 使用方式

文件用途方式
internal/service/iam.go:76GetMe() 获取当前用户authn.PrincipalFromContext
internal/service/iam.go:159requireZonePermission() SpiceDB 检查authn.PrincipalFromContext
internal/service/authz_admin.go:201requireGlobalAuthz() 全局权限检查authn.PrincipalFromContext
internal/server/group_http.go:171runWithGatewayPrincipal() HTTP handlerauthn.PrincipalFromContext
internal/service/control_plane.go:507currentPrincipalSubject() 控制面辅助authn.PrincipalFromContext

6.4 PR #29 修复:控制面 Actor 来源

写操作修复前修复后
CreateOrganization.Ownerreq.GetOwner()强制来自 ctx Principal
CreateProject.CreatedBy请求体来自 ctx Principal
UpsertResource.CreatedByin.GetCreatedBy()来自 ctx Principal
BindResource.CreatedBy请求体来自 ctx Principal
GrantAccess.CreatedBy请求体来自 ctx Principal
RevokeAccess.Actor请求体来自 ctx Principal

七、业务使用规则

7.1 推荐方式

// ✅ 推荐:authn.PrincipalFromContext 是业务 authn/authz 主入口
p, ok := authn.PrincipalFromContext(ctx)
if !ok {
return nil, authn.ErrUnauthenticated("")
}
userID := p.SubjectID // 用户稳定 UUID
orgID := p.OrgID // Casdoor owner / Aisphere org 投影
email := p.Email
name := p.Name

// ✅ contextx.PrincipalFromContext 现在也是无损的
cp := contextx.PrincipalFromContext(ctx)
cp.OrgID // ✅ 不再丢失
cp.Username // ✅ 不再丢失
cp.Email // ✅ 不再丢失

7.2 禁止方式

// ❌ 不要直接解析 Gateway header
// 业务 handler 不应该解析 x-aisphere-* header
// Kernel middleware 会自动完成 trusted headers → Principal → ctx 注入

// ❌ 不要信任请求体中的 owner/created_by/actor
// 控制面写操作的 Actor 必须从 ctx Principal 获取

八、验证记录

8.1 单元测试

# Gateway claim headers → Principal 路径
go test ./authn -v -run "TestPrincipalFromGateway"
# PASS: TestPrincipalFromGatewayClaimHeaders
# PASS: TestPrincipalFromGatewayClaimHeadersCasdoorProfileClaims
# PASS: TestPrincipalFromGatewayClaimHeadersPrefersInternalProjection
# PASS: TestPrincipalFromGatewayClaimHeadersRequiresSubject

# contextx 无损镜像
go test ./contextx -v -run "Authn"
# PASS: TestWithAuthnPrincipalMirrorsAllFields

# IAM 控制面 Actor 来源
go test ./internal/service/ -v -run "PrincipalContext|CurrentPrincipal"
# PASS: TestCurrentPrincipalSubjectUsesKernelAuthnContext
# PASS: TestCurrentPrincipalSubjectRejectsMissingPrincipal

8.2 端到端验证

# 模拟 Envoy 注入的完整 header 集
go run ./examples/authn-casdoor/cmd/check_gateway_principal/
# ALL TESTS PASSED

# 直接验证 Casdoor JWT
go run ./examples/authn-casdoor/cmd/check_principal/ -config config.remote.yaml
# Principal 字段完整:SubjectID, Username, Name, Email, Phone, OrgID...

九、相关 PR

PR仓库说明
#27kernelcontextx.Principal 无损镜像 + Gateway claim 读取增强
#29aisphere-iam控制面写操作 Actor 来自 ctx Principal
-aisphere-iam-front身份源提取到 Topbar,树形组织管理

十、附录:调试工具

10.1 check_principal

直接验证 Casdoor JWT → Principal 映射:

cd kernel
go run ./examples/authn-casdoor/cmd/check_principal/ \
-config examples/authn-casdoor/config.remote.yaml

10.2 check_gateway_principal

模拟 Envoy claimToHeaders 注入,验证 Gateway → Principal 路径:

cd kernel
go run ./examples/authn-casdoor/cmd/check_gateway_principal/