You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 18:54:50