ServerConfig
*di.ServerConfig 是宿主向 wicket 注入数据、存储和扩展实现的唯一入口。本页按用途分组列出 v0.1.4 的全部导出方法(di/server_config.go),并说明各方法何时被读取——这决定了运行中修改它是否生效。
root, err := di.NewRoot()
if err != nil { /* ... */ }
defer root.Close()
cfg := root.ServerConfig()
// ... 一系列 Set*
cfg.Freeze() // 装配完成ServerConfig 的每个 SetX 都有对应的读取方法 X(),读取方法供容器内部使用,宿主一般不需要调用,下文不再逐一列出。
Set 与 Provider 变体
大多数注入缝同时提供两种写法:
SetX(value):直接给值。内部等价于SetXProvider(func() T { return value })。SetXProvider(func() T):给一个函数,由容器在需要时调用。
两者的差别不在写法,而在容器调用该函数的时机。v0.1.4 中分为三类:
| 类别 | 涉及的方法 | 何时调用 Provider | 实际含义 |
|---|---|---|---|
| 配置数据 | Clients、IdentityScopes、ApiResources、ApiScopes、SigningCredentialStores、ValidationKeysStores | 每次解析相关服务时(瞬态) | Provider 可以返回最新数据,实现不重启的热更新 |
| 存储注入缝 | 除授权参数消息存储外的 10 个 store | 首次使用时调用一次,结果被缓存到进程结束 | Provider 只是“延迟构造”;之后再调用 SetX 不会生效 |
| 授权参数消息存储 | AuthorizationParametersMessageStore | 每次解析时 | 返回 nil 表示不使用存储(见下文) |
下面的片段已编译运行验证:
// 客户端列表可热更新:每次解析 ClientStore 都会调用这个函数
var clients atomic.Pointer[[]*models.Client]
initial := []*models.Client{}
clients.Store(&initial)
cfg.SetClientsProvider(func() []*models.Client { return *clients.Load() })
// 存储注入缝:首次使用时调用一次,结果被缓存
cfg.SetSessionStoreProvider(func() session.Store {
return session.NewMemoryStore(wallClock{}) // 换成你的持久化适配器
})热更新时注意
配置数据类 Provider 在请求路径上被频繁调用(ClientStore 每次解析都会重建索引并做重复 id 校验),函数本身要快且并发安全,不要在里面查数据库;在后台刷新一份快照、Provider 只读快照即可。
日志
| 方法 | 说明 |
|---|---|
SetLogger(*slog.Logger) | 设置容器内部组件使用的 logger |
v0.1.4 已知问题:SetLogger 对容器内部组件不生效
容器内部的 *slog.Logger 在 di.NewRoot() 执行时就已确定。而 ServerConfig 是由 NewRoot() 创建的,宿主不可能在此之前调用 SetLogger,所以内部组件实际使用的总是默认 logger:JSON 格式、Debug 级别、带源码位置、写到标准输出。server.WithLogger(...) 只影响 Server 自身的少量日志。
实测:快速开始示例设置了文本格式的 logger,但端点路由、客户端校验等内部日志仍以 JSON 形式出现在 stdout。宿主目前无法调整这一路日志的级别。v0.1.4 的部分日志可能包含敏感请求参数,生产部署时请把 stdout 接入日志系统,在采集侧按级别过滤、删除敏感字段,并避免把原始日志发往不受信任的系统,详见 日志与可观测性。
客户端与资源
| 方法 | 类型 | 说明 |
|---|---|---|
SetClients / SetClientsProvider | []*models.Client | 客户端注册表。内部包一层校验后作为 ClientStore |
SetIdentityScopes / SetIdentityScopesProvider | []*models.IdentityResource | 身份资源(openid、profile 等) |
SetApiResources / SetApiResourcesProvider | []*models.ApiResource | API 资源;内省端点用它的名称和 ApiSecrets 认证调用方 |
SetApiScopes / SetApiScopesProvider | []*models.ApiScope | API scope |
v0.1.4 的客户端与资源只能通过这几个方法注入,没有可替换的 ClientStore / ResourceStore 注入缝。需要从数据库加载时,用 Provider 变体返回快照。字段含义见客户端与资源。
签名与验证密钥
| 方法 | 类型 | 说明 |
|---|---|---|
SetSigningCredentialStores / SetSigningCredentialStoresProvider | []storage.SigningCredentialStore | 签名凭据来源 |
SetValidationKeysStores / SetValidationKeysStoresProvider | []storage.ValidationKeysStore | 验证公钥来源,合并后发布到 JWKS |
SetRecordStore / SetRecordStoreProvider | keymgmt.RecordStore | 开启自动密钥管理(opts.KeyManagement.Enabled,默认开启)时,密钥记录的持久化位置 |
注意方法名是 SetRecordStore,不是 SetKeyRecordStore。两种密钥来源如何取舍见签名密钥。
存储注入缝
方法(均有 *Provider 变体) | 接口 | 未注入且允许回退时 |
|---|---|---|
SetAuthorizationCodeStore | storage.AuthorizationCodeStore | 内存实现 + 警告日志 |
SetRefreshTokenStore | storage.RefreshTokenStore | 同上 |
SetReferenceTokenStore | storage.ReferenceTokenStore | 同上 |
SetUserConsentStore | storage.UserConsentStore | 同上 |
SetPersistedGrantStore | storage.PersistedGrantStore | 同上 |
SetDeviceFlowStore | storage.DeviceFlowStore | 同上 |
SetBackchannelAuthenticationRequestStore | storage.BackchannelAuthenticationRequestStore | 同上 |
SetSessionStore | session.Store | 同上 |
SetDistributedCache | services.DistributedCache | 同上 |
SetRecordStore | keymgmt.RecordStore | 同上 |
SetAuthorizationParametersMessageStore | storage.AuthorizationParametersMessageStore | 不回退:不使用存储,授权参数直接放在回调 URL 的查询串里 |
以上 11 项都计入 Freeze() 的完整性检查。接口方法与契约见存储端口。
资源释放:root.Close() 会按构造的逆序关闭已经实际用到的 store;若你的适配器实现了 io.Closer,其 Close() 会被调用。从未被使用的 store 不会被构造也不会被关闭。授权参数消息存储例外——它不经容器包装,由宿主自己负责关闭。
扩展点
| 方法 | 作用 | 未设置时 |
|---|---|---|
SetProfileProvider(func() services.ProfileService) | 提供用户资料:令牌与 userinfo 中的 claims(GetProfileData)、用户是否仍有效(IsActive)。每次解析时调用,返回 nil 则使用默认实现 | 默认实现不添加任何 claim,且 IsActive 恒为 true。需要 name、email 等用户 claim 时必须注入 |
SetResourceOwnerValidator(validators.ResourceOwnerPasswordValidator) | 启用 password 授权并校验用户名密码 | 一律拒绝(NotSupportedResourceOwnerPasswordValidator),发现文档也不公布 password |
SetBackchannelAuthenticationUserValidator(validators.BackchannelAuthenticationUserValidator) | CIBA 请求中根据 login_hint 等定位用户 | 默认实现仅供开发联调,不会解析或核验用户提示——生产环境必须注入自己的实现 |
SetAuthenticationHandlerProvider(scheme, func(godi.Provider) auth.Handler) | 为某个认证方案名提供 handler 工厂,覆盖内置注册 | 使用内置 handler,详见身份接入概览 |
SetExternalFailurePath(string) | 外部登录失败时的重定向路径 | 空字符串(不重定向) |
SetBackChannelLogoutHttpClient(*http.Client) | 后端通道登出通知所用的 HTTP 客户端 | nil |
SetBackchannelAuthenticationNotificationHttpClient(*http.Client) | CIBA 通知所用的 HTTP 客户端 | http.DefaultClient(无超时) |
后端通道登出通知在 v0.1.4 不会发送
负责发送通知的 BackchannelLogoutService.SendLogoutNotificationsAsync 目前是空实现,直接返回 nil,所以 SetBackChannelLogoutHttpClient 暂时没有可观察的效果——尽管发现文档声明了 backchannel_logout_supported: true。依赖后端通道登出的 RP 需要另行处理,参见会话与登出。
CIBA 通知客户端默认是没有超时的 http.DefaultClient,建议注入一个设置了超时的 *http.Client。
生命周期
| 方法 | 说明 |
|---|---|
AllowInMemoryFallback() | 声明“未注入的存储可以回退到内存实现”。仅用于本地开发与测试 |
Freeze() | 标记装配完成。若未调用 AllowInMemoryFallback(),逐项检查上表 11 个存储注入缝,任一未注入即 panic |
Frozen() bool | 是否已调用过 Freeze() |
未调用 AllowInMemoryFallback() 时的实测 panic 信息:
panic: store seam "authorization parameters message store" not injected by host and in-memory fallback not declared几点说明:
- 生产装配应当不调用
AllowInMemoryFallback(),让漏注入在启动时暴露。多副本部署下,任何一个存储落在进程内存里都会导致跨节点请求随机失败。 - 调用
Freeze()之后,每个存储在首次使用时还会再做一次已冻结检查;但如果从未调用Freeze(),这项检查不会触发,未注入的存储会静默回退到内存。所以请总是调用Freeze()。 Freeze()不会锁住各个Set*方法——之后调用它们不会报错,但存储类注入缝一旦被使用就不再重新读取。请把所有注入集中在Freeze()之前、开始服务之前完成。