Keycloak 26中重写OIDCClientRegistrationProvider处理自定义字段
问题背景
在Keycloak 15中正常运行的自定义OIDC客户端注册扩展(用于处理请求体中的webhook_uris字段),升级到Keycloak 26后失效:请求仍进入内置的OIDCClientRegistrationProvider而非自定义实现,返回错误:
{
"error": "Invalid json representation for OIDCClientRepresentation. Unrecognized field "webhook_uris" at line 25 column 26."
}
已完成的前置操作:
- 自定义了
CustomOIDCClientRegistrationProviderFactory、CustomOIDCClientRegistrationProvider、CustomOIDCClientRepresentation类 - 在
resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory中注册了自定义SPI - 日志已验证SPI注册成功:
2025-08-05 18:38:19,821 WARN [org.key.services] (build-2) KC-SERVICES0047: openid-connect (com.connector.CustomOIDCClientRegistrationProviderFactory) is implementing the internal SPI client-registration. This SPI is internal and may change without notice
现有代码
CustomOIDCClientRegistrationProviderFactory.java
public class CustomOIDCClientRegistrationProviderFactory extends OIDCClientRegistrationProviderFactory { @Override public String getId() { return "openid-connect"; } @Override public int order() { return 100; } @Override public OIDCClientRegistrationProvider create(KeycloakSession session) { return new CustomOIDCClientRegistrationProvider(session); } }
CustomOIDCClientRegistrationProvider.java
public class CustomOIDCClientRegistrationProvider extends OIDCClientRegistrationProvider { public CustomOIDCClientRegistrationProvider(KeycloakSession session) { super(session); } @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response createCustomOIDC(CustomOIDCClientRepresentation customOIDCClientRepresentation) { return Response.ok().entity(customOIDCClientRepresentation).build(); } }
CustomOIDCClientRepresentation.java
@JsonIgnoreProperties(ignoreUnknown = true) public class CustomOIDCClientRepresentation extends OIDCClientRepresentation { @JsonProperty("webhook_uris") private String webhook_uris; public String getWebhook_uris() { return webhook_uris; } public CustomOIDCClientRepresentation setWebhook_uris(String webhook_uris) { this.webhook_uris = webhook_uris; return this; } }
修复步骤
Keycloak 26对client-registration SPI的加载逻辑和请求路由规则做了调整,需修改以下内容:
1. 调整自定义Factory的加载优先级
内置OIDCClientRegistrationProviderFactory的order()返回值为0,自定义Factory需要返回小于0的值才能优先被加载:
@Override public int order() { return -1; // 比内置实现优先级更高 }
2. 重写父类的注册方法,而非新增方法
自定义Provider中新增的createCustomOIDC方法不会被框架路由,必须重写父类原有的create方法,并在方法内处理自定义字段:
public class CustomOIDCClientRegistrationProvider extends OIDCClientRegistrationProvider { public CustomOIDCClientRegistrationProvider(KeycloakSession session) { super(session); } @Override @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response create(OIDCClientRepresentation rep) { // 将内置Representation转换为自定义类型,解析webhook_uris字段 ObjectMapper mapper = new ObjectMapper(); CustomOIDCClientRepresentation customRep = mapper.convertValue(rep, CustomOIDCClientRepresentation.class); // 将自定义字段存储到客户端属性中(避免修改Keycloak内部实体结构) if (customRep.getWebhook_uris() != null) { if (rep.getAttributes() == null) { rep.setAttributes(new HashMap<>()); } rep.getAttributes().put("webhook_uris", customRep.getWebhook_uris()); } // 调用父类方法完成默认注册流程 return super.create(rep); } }
3. (可选)重写查询方法返回自定义字段
如果需要在查询客户端详情时返回webhook_uris,需重写get方法,将存储在属性中的值映射回自定义Representation:
@Override @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) public Response get(@PathParam("id") String id) { Response originalResponse = super.get(id); OIDCClientRepresentation rep = (OIDCClientRepresentation) originalResponse.getEntity(); CustomOIDCClientRepresentation customRep = new CustomOIDCClientRepresentation(); // 拷贝原有字段 BeanUtils.copyProperties(rep, customRep); // 从属性中读取自定义字段 if (rep.getAttributes() != null) { customRep.setWebhook_uris(rep.getAttributes().get("webhook_uris")); } return Response.ok(customRep).build(); }
关键注意事项
- Keycloak 20+版本调整了内部SPI的加载优先级规则,自定义Factory必须通过更低的
order()值抢占内置实现的加载顺序 - 不能仅新增自定义接口方法,必须重写父类中已有的对应HTTP方法(如
create、update)才能拦截请求 - 自定义字段建议存储在客户端的
attributes集合中,避免直接修改Keycloak内部实体结构引发兼容性问题
内容的提问来源于stack exchange,提问作者Siddharth Eswaramoorthy

