基于Keycloak 21.1.1与Spring Boot,如何预设置令牌中userId自定义声明?
在Keycloak 21.1.1中程序化添加userId自定义令牌声明
针对你的需求,有两种实用方案可以实现:一种是复用现有User Attribute映射器,通过自定义Authenticator在登录时设置用户属性;另一种是直接自定义Protocol Mapper生成声明。
方案一:通过自定义Authenticator设置用户属性(复用现有映射器)
此方案利用你已配置的User Attribute映射器,在登录流程中动态给用户设置userId属性,映射器会自动将其同步到令牌。
1. 实现自定义Authenticator及工厂类
首先编写Authenticator逻辑,在用户名密码验证通过后设置用户属性:
import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.authenticators.browser.AbstractUsernamePasswordAuthenticator; import org.keycloak.models.UserModel; public class SetUserIdAuthenticator extends AbstractUsernamePasswordAuthenticator { @Override public void authenticate(AuthenticationFlowContext context) { // 执行默认用户名密码验证 super.authenticate(context); UserModel user = context.getUser(); if (user != null) { // 这里替换成你的userId生成逻辑,示例用Keycloak内置用户ID String userIdValue = user.getId(); // 避免重复设置属性 if (user.getFirstAttribute("userId") == null) { user.setSingleAttribute("userId", userIdValue); } } context.success(); } }
然后编写工厂类用于注册Authenticator:
import org.keycloak.Config; import org.keycloak.authentication.Authenticator; import org.keycloak.authentication.AuthenticatorFactory; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.provider.ProviderConfigProperty; import java.util.List; public class SetUserIdAuthenticatorFactory implements AuthenticatorFactory { public static final String ID = "set-user-id-authenticator"; private static final SetUserIdAuthenticator SINGLETON = new SetUserIdAuthenticator(); @Override public String getId() { return ID; } @Override public String getDisplayType() { return "Set User ID Attribute"; } @Override public String getHelpText() { return "Sets the userId attribute for users during login"; } @Override public Authenticator create(KeycloakSession session) { return SINGLETON; } @Override public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { return new AuthenticationExecutionModel.Requirement[]{ AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED }; } @Override public String getReferenceCategory() { return "authentication"; } @Override public boolean isConfigurable() { return false; } @Override public boolean isUserSetupAllowed() { return false; } @Override public List<ProviderConfigProperty> getConfigProperties() { return null; } @Override public void init(Config.Scope config) {} @Override public void postInit(KeycloakSessionFactory factory) {} @Override public void close() {} }
2. 注册并部署扩展
- 在项目的
META-INF/services目录下创建文件org.keycloak.authentication.AuthenticatorFactory,添加一行:
(替换为你实际的包路径)com.yourpackage.SetUserIdAuthenticatorFactory - 将代码打包成JAR,放入Keycloak的
providers目录 - 重启Keycloak,执行构建命令:
# Linux/macOS ./kc.sh build # Windows kc.bat build
3. 配置登录流程
- 进入Keycloak管理控制台,选择目标Realm
- 左侧菜单点击
Authentication→Flows,复制默认的Browser流程(命名如Browser-with-UserId) - 展开复制后的流程,找到
Username Password Form,点击Add execution - 在列表中选择
Set User ID Attribute,设置其Requirement为REQUIRED,并移动到Username Password Form之后 - 进入你的客户端设置,将
Authentication Flow Overrides中的Browser Flow改为自定义的Browser-with-UserId
方案二:自定义Protocol Mapper直接生成声明
如果不想依赖用户属性,可以直接编写Protocol Mapper,在令牌生成时动态添加userId声明。
1. 实现自定义Protocol Mapper
import org.keycloak.models.ClientSessionContext; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper; import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper; import org.keycloak.provider.ProviderConfigProperty; import java.util.List; public class UserIdProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { public static final String PROVIDER_ID = "user-id-protocol-mapper"; @Override public String getId() { return PROVIDER_ID; } @Override public String getDisplayType() { return "User ID Custom Claim"; } @Override public String getHelpText() { return "Adds a custom 'userId' claim to access tokens and ID tokens"; } @Override protected void setClaim(UserSessionModel userSession, ProtocolMapperModel mappingModel, ClientSessionContext clientSessionCtx) { // 替换为你的userId生成逻辑 String userIdValue = userSession.getUser().getId(); OIDCAttributeMapperHelper.mapClaim(userSession, mappingModel, clientSessionCtx, userIdValue); } @Override public List<ProviderConfigProperty> getConfigProperties() { List<ProviderConfigProperty> props = OIDCAttributeMapperHelper.createClaimConfigProperties(); OIDCAttributeMapperHelper.setNameClaimDefaultValue(props, "userId"); return props; } @Override public String getDisplayCategory() { return OIDCAttributeMapperHelper.TOKEN_MAPPER_CATEGORY; } }
2. 注册并部署扩展
- 在
META-INF/services目录下创建文件org.keycloak.protocol.ProtocolMapper,添加一行:com.yourpackage.UserIdProtocolMapper - 打包JAR放入Keycloak的
providers目录,重启并执行kc.sh build
3. 配置客户端Mapper
- 进入目标客户端的
Client scopes标签,选择客户端专属的scope(如your-client-dedicated) - 点击
Add mapper→By configuration,选择User ID Custom Claim - 配置时确认Claim name为
userId,勾选要添加的令牌类型(Access Token/ID Token),保存即可
依赖说明
开发时需引入Keycloak SPI依赖,Maven配置示例:
<dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-core</artifactId> <version>21.1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-server-spi</artifactId> <version>21.1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-server-spi-private</artifactId> <version>21.1.1</version> <scope>provided</scope> </dependency>
内容的提问来源于stack exchange,提问作者Tatu
相关产品推荐
相关产品推荐

