ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

keycloak~登录时将请求头里某个属性放入UserSessionModel

keycloak~登录时将请求头里某个属性放入UserSessionModel

UserSessionModel做为用户登录过程中的一个会话,可以用来跨flow使用数据,这些数据被保存到内存里,在认证过程中可以被使用,今天的一个需求要求在登录时从请求头获取IP所在地并写到kafka里,要想实现这个需求,你可以在现有认证流程中修改代码,但不建议这样做,因为这种修改对原始逻辑会有破坏,keycloak提供了自定义认证流,并在后台可以灵活的配置。

图片

从上面图中可以看到,这个登录的过程会经历多个认证流,在所有被开启的认证流执行完成后才算登录成功,而这些流程我们是可以进行按需开发并配置的,下面说一下keycloak认证过程的几大事件,以表单登录为例(社区三方认证流程更复杂一些:

  1. 表单提交
  2. 标准用户密码认证流执行
  3. 扩展认证流执行
  • 会话限制 User Session Count Limiter
  • 请求头到session的转换 Header-session-authenticator
  • 黑名单控制 BlackListFilterAuthenticator
  • 用户有效性控制 User Validate
  • 弱密码提醒 Config Simple Password Alert Form
  • MFA多因子认证 OTP Form
  1. 执行jwt token构建流程,包含自定义的AbstractOIDCProtocolMapper
  2. 发布Login登录成功事件
  3. 订阅了Login事件的监听器可以写入kafka消息

下面自定义一个从请求头获取属性写入userSessionModel的例子

  @JBossLog
public class RequestHeaderToSessionNoteAuthenticator implements Authenticator {private final KeycloakSession session;public RequestHeaderToSessionNoteAuthenticator(KeycloakSession session) {this.session = session;}@Overridepublic void authenticate(AuthenticationFlowContext context) {HttpHeaders httpHeaders = context.getHttpRequest().getHttpHeaders();if (httpHeaders.getRequestHeaders().containsKey(UserUtils.EO_CLIENT_REGIONNAME)) {context.getAuthenticationSession().setUserSessionNote("lastLoginProvince",URLDecoder.decode(httpHeaders.getHeaderString(UserUtils.EO_CLIENT_REGIONNAME)));context.getEvent().detail("lastLoginProvince",URLDecoder.decode(httpHeaders.getHeaderString(UserUtils.EO_CLIENT_REGIONNAME)));}if (httpHeaders.getRequestHeaders().containsKey(UserUtils.EO_CLIENT_CITYNAME)) {context.getAuthenticationSession().setUserSessionNote("lastLoginCity",URLDecoder.decode(httpHeaders.getHeaderString(UserUtils.EO_CLIENT_CITYNAME)));context.getEvent().detail("lastLoginCity",URLDecoder.decode(httpHeaders.getHeaderString(UserUtils.EO_CLIENT_CITYNAME)));}context.success();}private EntityManager getEntityManager() {return this.session.getProvider(JpaConnectionProvider.class).getEntityManager();}@Overridepublic void action(AuthenticationFlowContext context) {}@Overridepublic boolean requiresUser() {return true;}@Overridepublic boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {return false;}@Overridepublic void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {}
}public class RequestHeaderToSessionNoteAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory {public final static String PROVIDER_ID = "header-session-authenticator";@Overridepublic String getDisplayType() {return "header-session-authenticator";}@Overridepublic String getReferenceCategory() {return null;}@Overridepublic boolean isConfigurable() {return false;}@Overridepublic AuthenticationExecutionModel.Requirement[] getRequirementChoices() {return REQUIREMENT_CHOICES;}@Overridepublic boolean isUserSetupAllowed() {return true;}@Overridepublic String getHelpText() {return "header-session-authenticator";}@Overridepublic List<ProviderConfigProperty> getConfigProperties() {return null;}@Overridepublic Authenticator create(KeycloakSession keycloakSession) {return new RequestHeaderToSessionNoteAuthenticator(keycloakSession);}@Overridepublic void init(Scope scope) {}@Overridepublic void postInit(KeycloakSessionFactory keycloakSessionFactory) {}@Overridepublic void close() {}@Overridepublic String getId() {return PROVIDER_ID;}
}

最后在resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory中添加你的这个Factory即可。

返回列表