这块将异常报错复原了,如果不报错,将会出现大量session从数据库加载,导致数据库崩盘
对在线token的校验去掉了https和http的校验只要后面域名相同就是ISSIssuer相同就行这块在验证token时会用到另外在适配器集成中每人请求加载前也会用到它调查code码被占用问题生产了重复的code码)1 code的生产2 code的校验解决同时打开两个登录窗口在第一个窗口登录后在第二个窗口再登录一次会出现“您已经登录”的页面解决发生上面的情况后直接跳到v6首页LoginActionsServiceChecks.checkNotLoggedInYet()方法SessionCodeChecks.initialVerifyAuthSession()方法让IdentityProviderMapper实现的类型自动为社区登录执行delegateUpdateBrokeredUser修改源代码org.keycloak.services.resources.updateFederatedIdentity()添加了代码逻辑实现了按需自动执行//对已有用户进行更新注意可能会覆盖用户的其它属性 FederatedIdentityModel finalFederatedIdentityModel federatedIdentityModel; sessionFactory.getProviderFactoriesStream(IdentityProviderMapper.class) .map(IdentityProviderMapper.class::cast) .map(mapper - Arrays.stream(mapper.getCompatibleProviders()) .filter(type - Objects.equals(finalFederatedIdentityModel.getIdentityProvider(), type)) .map(type - mapper) .findFirst() .orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toMap(IdentityProviderMapper::getId, Function.identity())) .forEach((a, b) - { IdentityProviderMapper target (IdentityProviderMapper) sessionFactory .getProviderFactory(IdentityProviderMapper.class, a); IdentityProviderMapperModel identityProviderMapperModel new IdentityProviderMapperModel(); identityProviderMapperModel.setConfig(new HashMap()); identityProviderMapperModel.setSyncMode(IdentityProviderMapperSyncMode.FORCE); identityProviderMapperModel.setId(a); identityProviderMapperModel.setIdentityProviderMapper(finalFederatedIdentityModel.getIdentityProvider()); identityProviderMapperModel.setIdentityProviderAlias(finalFederatedIdentityModel.getIdentityProvider()); try { if (!Objects.equals(target.getId(), UsernameTemplateMapper.PROVIDER_ID)) { IdentityProviderMapperSyncModeDelegate.delegateUpdateBrokeredUser(session, realmModel, federatedUser, identityProviderMapperModel, context, target); } } catch (RuntimeException ex) { } });添加一个例子实现社区登录的类型自动存储到用户属性loginType中,getCompatibleProviders()方法中绑定了IdentityProviderMapper.ANY_PROVIDER所以在每个社区登录后它都会被执行新用户不会绑定这个已绑定的用户才执行这个方法,原因是syncModel为Force,表示当有用户后会强制更新它public class V6UserAttributeMapper extends AbstractJsonUserAttributeMapper { public static final String PROVIDER_ID v6-user-attribute-mapper; private static final String[] cp new String[] {IdentityProviderMapper.ANY_PROVIDER}; Override public String[] getCompatibleProviders() { return cp; } Override public String getId() { return PROVIDER_ID; } Override public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { logger.info(updateBrokeredUser user info...); user.setSingleAttribute(loginType, mapperModel.getIdentityProviderAlias()); } }登录后更新用户属性loginType登录后将loginType添加到refresh_token中解决由于kc的refresh_token不支持自定义属性所以在登录后将loginType添加到refresh_token中这样在refresh_token时就可以获取到loginType了实现逻辑将当前loginType添加到当前refresh_token在下次刷新token时将refresh_token里的loginType取出来覆盖到新的access_token里.org.keycloak.protocol.oidc.TokenManager.validateToken()org.keycloak.protocol.oidc.TokenManager.build()用户session_state生成方式org.keycloak.models.sessions.infinispan.createUserSession()解决用户浏览器因为丢失keycloak_identity而keycloak_session_id有并且是在线的导致无法登录的问题在方法AuthorizationEndpointBase.createAuthenticationSession()添加了判断逻辑没有keycloak_identity就重新根据session_id再生成一个到cookie里Cookie cookie CookieHelper.getCookie(headers.getCookies(), KEYCLOAK_IDENTITY_COOKIE); if (cookie null) { cookie CookieHelper.getCookie(headers.getCookies(), KEYCLOAK_IDENTITY_COOKIE CookieHelper.LEGACY_COOKIE); if (cookie null) { AuthenticationManager.createLoginCookie(session, realm, user, userSession, session.getContext().getUri(), session.getContext().getConnection()); } }关于对loginType和登录事件的修改请查看TODO: 20230406的注释代码涉及到以下动作会触发的事件会添加我们扩展的属性共享登录code换token刷新token关于OTP提供商的调研OTP提供商的策略org.keycloak.models.OTPPolicy目前支持FreeOTP和GoogleAuthenticator关于keycloak-services项目添加第三方jar包的问题我们例如将org.infinispan这个包在kc里也是一个module引用到keycloak-services项目它在启动时会报错告诉找不到这个org.infinispan.Cache类类似这种类无法找到的错误。Uncaught server error: java.lang.NoClassDefFoundError: org/infinispan/Cache at org.keycloak.keycloak-services14.0.0//org.keycloak.protocol.oidc.TokenManager.checkTokenValidForIntrospection(TokenManager.java:494)解决思路在module.xml中添加对应的模块即可从keycloak容器里将/opt/jboss/keycloak/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml复制出来在文件的dependencies节点下添加依赖如module nameorg.infinispan/修改Dockerfile文件将这个module.xml文件也复制到上面的容器目录覆盖原来的文件重新构建镜像启动容器问题解决自动登录接口同一浏览器添加踢出之前登录的逻辑org.keycloak.protocol.AuthorizationEndpointBase.handleBrowserAuthenticationRequest()从carsi网站过来的用户会带有carsi-auto这个关键字也应该要踢出之前的登录org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider.Endpoint.authResponse()刷新token和通过code换token逻辑中添加infinispan缓存的逻辑键key是sessionIdclientId作用是当用户的角色变更后用户校验token直接返回false让迫使用户重新去刷新token// TODO: xxx_user_modify_role 需要添加逻辑去检索事件中是否包括了权限变更的用户验证token: org.keycloak.protocol.oidc.TokenManager.checkTokenValidForIntrospection()刷新token: org.keycloak.protocol.oidc.endpoints.TokenEndpoint.refreshTokenGrant,这块因为相同的event对象所以代码迁移到keycloak-services-event-kafkacode换token: org.keycloak.protocol.oidc.endpoints.TokenEndpoint.codeToToken(),这块因为相同的event对象所以代码迁移到keycloak-services-event-kafka用户权限更新后通和逻辑整理kc服务端收到REALM_ROLE_MAPPING或者USER_ROLE_CHANGE事件后向infinispan缓存里添加一个keykey是xxx_user_modify_role_{userId},value是空它有缓存有效期与access_token的相同目前为30分钟当用户进行code换token或者刷新token时根据当前用户id去上面缓存中找如果查找到说明这个用户的权限发生了变更找到后向这个缓存xxx_user_modify_role_{userId}添加valuevalue格式是{sessionId}_{clientId}就是用户在哪个浏览器哪个客户端访问当用户调用验证token接口时如果在xxx_user_modify_role_{userId}中没有找到这个value{sessionId}_{clientId}就验证失败当验证失败后返回401用户再去刷新token向xxx_user_modify_role_{userId}中添加对应的value, 保持下次验证会成功