- 后端
- 认证鉴权
- 单点登录
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
Apereo CAS 提供开箱即用的 Okta 属性解析能力,可通过 Okta 管理 API 按用户名(或其他登录属性)拉取用户档案与系统字段,将 Okta 作为 Person Directory 属性仓库注入属性解析计划。本文围绕 Attribute-Resolution-Okta.md 展开,结合 cas-server-support-okta-authentication 模块源码,完整讲解依赖引入、配置项含义、可解析的属性清单、底层调用链与测试验证方式,帮助你准确规划 CAS 中来自 Okta 的用户属性来源。
一、功能定位:Okta 作为 CAS 属性仓库
Okta 属性解析属于 CAS 的Attribute Resolution(属性解析)体系。与"认证"不同,属性解析解决的是"用户名已确定之后,从哪里取回该用户的属性"这一问题。在 CAS 中,所有属性源统称为 Person Directory(人员目录)中的属性仓库(Attribute Repository),每个仓库实现PersonAttributeDao接口,并按配置顺序注册到属性解析计划中。
Okta 属性解析模块的价值在于:
- 复用 Okta 作为企业级用户目录的既有数据,无需在 CAS 侧再维护一份用户档案;
- 由 Okta 官方 Java SDK(
com.okta.sdk)直接调用 Okta 管理 API,按用户 ID 查询用户对象; - 将 Okta 用户档案(Profile)与系统字段映射为带
okta前缀的 CAS 属性,供后续属性发布、服务属性策略等环节消费。
该功能自 6.4.0 版本开始引入(OktaPersonAttributeDao 的@since 6.4.0注释可佐证)。
二、依赖引入:cas-server-support-okta-authentication
在构建文件中加入以下模块即可启用 Okta 属性解析能力:
implementation "org.apereo.cas:cas-server-support-okta-authentication"该模块的配置模型 OktaPrincipalAttributesProperties 标注了@RequiresModule(name = "cas-server-support-okta-authentication"),说明属性解析功能与 Okta 认证功能同属一个支持模块。也就是说,引入这一个模块即可同时获得 Okta 认证与 Okta 属性解析两条能力线(本文聚焦属性解析)。
三、配置项详解:cas.authn.attribute-repository.okta.*
所有 Okta 属性解析配置均挂在cas.authn.attribute-repository.okta命名空间下。这些配置项由 OktaPrincipalAttributesProperties(继承自 BaseOktaApiProperties)定义。
3.1 基础连接配置
| 配置项 | 默认值 | 必填 | 说明 |
|---|---|---|---|
cas.authn.attribute-repository.okta.organization-url | 无 | 是 | Okta 组织域名 URL,如https://dev-668371.oktapreview.com。它是条件装配的开关属性(见下文源码分析),也是构建 Okta SDK 客户端的setOrgUrl输入。 |
cas.authn.attribute-repository.okta.api-token | 无 | 二选一 | Okta API Token,用于TokenClientCredentials方式的客户端认证。 |
cas.authn.attribute-repository.okta.client-id | 无 | 二选一 | Okta 应用客户端 ID,与private-key配合,走 OAuth 2.0 私有密钥授权模式。 |
cas.authn.attribute-repository.okta.private-key | 无 | 二选一 | 私有密钥资源位置(SpringResourceProperties,可为file:、classpath:等 Spring 资源),用于 OAuth 2.0 私有密钥方式调用 Okta API。 |
cas.authn.attribute-repository.okta.scopes | okta.users.read、okta.apps.read | 否 | 使用client-id+private-key时需要的 OAuth 2.0 作用域列表。默认已包含用户与应用读取权限。 |
cas.authn.attribute-repository.okta.connection-timeout | 见BaseOktaProperties | 否 | Okta SDK HTTP 客户端连接超时。 |
cas.authn.attribute-repository.okta.proxy-host | 无 | 否 | 通过代理访问 Okta 时的代理主机名。 |
cas.authn.attribute-repository.okta.proxy-port | 0 | 否 | 代理端口,小于等于 0 时视为不启用代理。 |
cas.authn.attribute-repository.okta.proxy-username/proxy-password | 无 | 否 | 代理认证凭据,二者同时非空时构造带认证的Proxy对象。 |
认证方式二选一:要么提供api-token,要么提供client-id+private-key(此时 Okta SDK 会自动为你换取访问令牌,无需 API Token),两条路径在 OktaConfigurationFactory#buildClient 中体现。
3.2 属性解析行为配置
| 配置项 | 默认值 | 必填 | 说明 |
|---|---|---|---|
cas.authn.attribute-repository.okta.username-attribute | username | 是 | 用于查询 Okta 用户(client.getUser(uid))的登录属性名。默认直接使用username。 |
cas.authn.attribute-repository.okta.id | 无 | 否 | 为当前属性解析器分配的唯一标识,用于区分多个属性仓库。 |
cas.authn.attribute-repository.okta.order | 继承 Person Directory 默认 | 否 | 该属性仓库在解析计划中的执行顺序,影响多属性源合并时的优先级。 |
一个最小可用的属性解析配置示例:
cas: authn: attribute-repository: okta: organization-url: https://dev-668371.oktapreview.com api-token: 0030j4HfPHEIQG39pl0nNacnx2bqqZMqDq6Hk5wfNa username-attribute: username order: 1如果组织采用 OAuth 2.0 私有密钥方式,则示例为:
cas: authn: attribute-repository: okta: organization-url: https://your-org.okta.com client-id: 0oa1abc2def3GHIjklmno private-key: file:/etc/cas/okta/private.pem scopes: - okta.users.read - okta.apps.read注意:private-key使用SpringResourceProperties,因此也支持classpath:okta/private.pem这类资源位置写法。
四、可解析的属性清单:Okta 数据模型到 CAS 属性映射
OktaPersonAttributeDao#getPerson 是属性获取的核心方法:它以uid调用 Okta SDK 的client.getUser(uid),随后将 Okta 用户对象上的系统字段与 Profile 档案字段逐一映射为 CAS 属性。
4.1 系统字段(User 对象)
| CAS 属性名 | Okta SDK 来源 | 说明 |
|---|---|---|
oktaUserId | user.getId() | Okta 用户唯一 ID |
oktaUserStatus | user.getStatus() | 用户状态(如ACTIVE) |
oktaUserType | user.getType() | 用户类型 |
oktaUserActivatedDate | user.getActivated() | 激活时间戳 |
oktaUserCreatedDate | user.getCreated() | 创建时间戳 |
oktaUserLastLoginDate | user.getLastLogin() | 最后登录时间戳 |
oktaUserLastUpdatedDate | user.getLastUpdated() | 最后更新时间戳 |
oktaUserPasswordChangedDate | user.getPasswordChanged() | 密码变更时间戳 |
时间类字段在源码中通过getTime()转为毫秒时间戳存入属性,属性名统一以oktaUser开头。
4.2 用户档案字段(Profile)
| CAS 属性名 | Okta Profile 字段 | 典型用途 |
|---|---|---|
oktaLogin | login | 登录名 |
oktaEmail | email | 主邮箱 |
oktaSecondEmail | secondEmail | 备用邮箱 |
oktaFirstName/oktaLastName/oktaMiddleName | 同名 Profile 字段 | 姓名拆分字段 |
oktaDisplayName/oktaNickName | 同名 Profile 字段 | 显示名与昵称 |
oktaPrefix/oktaSuffix | honorificPrefix/honorificSuffix | 称谓前后缀 |
oktaMobilePhone/oktaPrimaryPhone | 同名 Profile 字段 | 手机号 |
oktaStreetAddress/oktaCity/oktaState/oktaPostalAddress | 同名 Profile 字段 | 地址信息 |
oktaCountryCode/oktaLocale/oktaPreferredLanguage/oktaTimezone | 同名 Profile 字段 | 地区、语言、时区 |
oktaDepartment/oktaDivision/oktaOrganization/oktaCostCenter | 同名 Profile 字段 | 组织信息 |
oktaTitle | title | 职位 |
oktaEmployeeNumber | employeeNumber | 员工号 |
oktaManager/oktaManagerId | 同名 Profile 字段 | 直属上级及其 ID |
需要强调两点:
- 属性写入遵循"非空才写入"原则:源码中每个字段都经
FunctionUtils.doIfNotNull(...)包装,Okta 上未设置的 Profile 字段不会产生对应 CAS 属性; - 返回值通过
SimplePersonAttributes(uid, ...)构造,保留查询 uid 作为人员主标识,同时把全部属性经PersonAttributeDao.stuffAttributesIntoList(attributes)规范化为多值属性列表结构,供 CAS 属性合并框架统一处理。
五、源码级原理:从配置到属性解析计划的装配链路
Okta 属性仓库的装配完全由 OktaPersonDirectoryConfiguration 负责,其生效链路清晰可控:
条件开关:该配置类标注
@ConditionalOnFeatureEnabled(feature = CasFeatureModule.FeatureCatalog.PersonDirectory, module = "okta"),且内部使用BeanCondition.on("cas.authn.attribute-repository.okta.organization-url")作为装配条件——即只有配置了organization-url时,Okta 属性仓库相关 Bean 才会真正注册。未配置时通过otherwiseProxy()/BeanContainer::empty提供空代理,避免装配失败。客户端构建:
oktaPersonDirectoryClientBean 读取cas.authn.attribute-repository.okta.*配置,调用 OktaConfigurationFactory#buildClient 构建 Okta SDK 的com.okta.sdk.client.Client。构建逻辑会依次处理setOrgUrl、连接超时、API Token 凭据或私有密钥授权(AuthorizationMode.PRIVATE_KEY+clientId+ scopes),以及可选的代理配置(带认证与不带认证两种Proxy构造)。DAO 创建:
oktaPersonAttributeDaosBean 创建OktaPersonAttributeDao,注入 Okta 客户端,设置usernameAttributeProvider(取自username-attribute配置)与order,并在配置了id时赋予 DAO 唯一标识。注册到解析计划:
oktaAttributeRepositoryPlanConfigurer实现PersonDirectoryAttributeRepositoryPlanConfigurer,将上述 DAO 注册进全局属性仓库解析计划(plan::registerAttributeRepository),与 LDAP、JDBC 等其它属性仓库并列。
整个装配过程使用@RefreshScope,意味着配置变更后可通过 CAS 的配置刷新机制热更新客户端与 DAO,无需重启。
5.1 与 Okta 认证功能的边界
同一模块中另有cas.authn.okta.organization-url条件控制 OktaAuthenticationConfiguration 的认证装配(构建AuthenticationClient与OktaAuthenticationHandler)。二者配置前缀不同(cas.authn.okta.*与cas.authn.attribute-repository.okta.*)、构建的 SDK 客户端类型不同(认证用AuthenticationClients,属性解析用Clients),互不干扰。你完全可以只用属性解析而不启用 Okta 认证,只需配置attribute-repository.okta前缀下的各项即可。
六、测试验证:如何确认属性解析行为
模块测试 OktaPersonAttributeDaoTests 提供了两种可借鉴的验证思路:
真实 SDK 客户端装配测试:以
cas.authn.attribute-repository.okta.organization-url=https://dev-668371.oktapreview.com与api-token启动 Spring 上下文,断言oktaPersonAttributeDaos容器中恰好注册 1 个 DAO、getPossibleUserAttributeNames与getAvailableQueryAttributes为空(Okta 仓库属于"查询式"属性源,不预先声明可用属性名)。Mock 客户端行为测试:Mock
UserProfile(如返回cas@example.org邮箱、员工号)与User(如返回casuser、ACTIVE状态),注入oktaPersonDirectoryClient后断言attributeRepository.getPerson("casuser")能返回用户、getPeople(Map.of("username", "casuser"), ...)能按查询属性解析出人员。
这两个测试类同时揭示了实际运维中验证 Okta 属性解析的要点:连接串、Token/密钥凭据、以及username-attribute查询属性三者的正确配置是解析成功的前提。
七、实际应用建议
- 属性消费:解析出的
okta*属性会进入主属性池,可在 RegisteredServiceAttributeReleasePolicy 与属性定义(Attribute Definitions)中按需选择发布给下游应用,避免把所有 Okta 字段直接暴露。 - 多属性源合并:将
order与 CAS 全局属性仓库顺序(如 PrincipalAttributesProperties 所承载的合并策略)配合使用,可控制 Okta 属性与其它来源属性的覆盖优先级。 - 凭据安全:
api-token或private-key属于敏感信息,建议经 CAS 的配置安全机制(如 Vault、Jasypt 加密属性)注入,而非明文写入配置仓库。
八、延伸阅读
- 模块整体自动装配入口:CasOktaAuthenticationAutoConfiguration
- 属性 DAO 实现:OktaPersonAttributeDao
- 配置模型:OktaPrincipalAttributesProperties 与 BaseOktaApiProperties
- 测试用例:OktaPersonAttributeDaoTests
- CAS 属性解析体系总览:Attribute Resolution(同一文档目录下的入口文档)
- 后端
- 认证鉴权
- 单点登录
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
相关推荐
Apereo CAS 认证事件持久化至 InfluxDB:模块配置、数据模型与源码原理
Apereo CAS 认证事件持久化至 InfluxDB:模块配置、数据模型与源码原理 本文围绕 Apereo CAS 的 InfluxDB 认证事件存储方案展
后端认证鉴权单点登录Apereo CAS 中 SAML2 属性值类型(attributeValueTypes)的配置与源码实现解析
Apereo CAS 中 SAML2 属性值类型(attributeValueTypes)的配置与源码实现解析 在 Apereo CAS 作为 SAML2 Id
后端认证鉴权单点登录Apereo CAS 属性释放之默认 Principal Id:DefaultRegisteredServiceUsernameProvider 配置与源码解析
Apereo CAS 属性释放之默认 Principal Id:DefaultRegisteredServiceUsernameProvider 配置与源码解析
后端认证鉴权单点登录
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考