keycloak~登錄時將請求頭里某個屬性放入UserSessionModel
UserSessionModel做為用戶登錄過程中的一個會話,可以用來跨flow使用數(shù)據(jù),這些數(shù)據(jù)被保存到內(nèi)存里,在認證過程中可以被使用,今天的一個需求要求在登錄時從請求頭獲取IP所在地并寫到kafka里,要想實現(xiàn)這個需求,你可以在現(xiàn)有認證流程中修改代碼,但不建議這樣做,因為這種修改對原始邏輯會有破壞,keycloak提供了自定義認證流,并在后臺可以靈活的配置。

相關(guān)keycloak中的知識
認證流程的執(zhí)行動作
從上面圖中可以看到,這個登錄的過程會經(jīng)歷多個認證流,在所有被開啟的認證流執(zhí)行完成后才算登錄成功,而這些流程我們是可以進行按需開發(fā)并配置的,下面說一下keycloak認證過程的幾大事件,以表單登錄為例(社區(qū)三方認證流程更復(fù)雜一些:
- 表單提交
- 標準用戶密碼認證流執(zhí)行
- 擴展認證流執(zhí)行
- 會話限制 User Session Count Limiter
- 請求頭到session的轉(zhuǎn)換 Header-session-authenticator
- 黑名單控制 BlackListFilterAuthenticator
- 用戶有效性控制 User Validate
- 弱密碼提醒 Config Simple Password Alert Form
- MFA多因子認證 OTP Form
- 執(zhí)行jwt token構(gòu)建流程,包含自定義的
AbstractOIDCProtocolMapper等 - 發(fā)布Login登錄成功事件
- 訂閱了Login事件的監(jiān)聽器可以寫入kafka消息
keycloak認證流程相關(guān)元素
- 瀏覽器認證流Browser Flow 繼承AbstractUsernameFormAuthenticator類
- 直接認證流Direct Grant Flow 繼承BaseDirectGrantAuthenticator類
- 用戶所需要動作Require Action 實現(xiàn)RequiredActionProvider接口
- 表單頁面Form Action,實現(xiàn)了FormAction接口
實現(xiàn)步驟
下面自定義一個從請求頭獲取屬性寫入userSessionModel的例子
@JBossLog
public class RequestHeaderToSessionNoteAuthenticator implements Authenticator {
private final KeycloakSession session;
public RequestHeaderToSessionNoteAuthenticator(KeycloakSession session) {
this.session = session;
}
@Override
public 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();
}
@Override
public void action(AuthenticationFlowContext context) {
}
@Override
public boolean requiresUser() {
return false;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return false;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
}
public class RequestHeaderToSessionNoteAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory {
public final static String PROVIDER_ID = "header-session-authenticator";
@Override
public String getDisplayType() {
return "header-session-authenticator";
}
@Override
public String getReferenceCategory() {
return null;
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}
// 是否針對用戶有require action動作,如果沒有,requiresUser()返回也為false
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override
public String getHelpText() {
return "header-session-authenticator";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
@Override
public Authenticator create(KeycloakSession keycloakSession) {
return new RequestHeaderToSessionNoteAuthenticator(keycloakSession);
}
@Override
public void init(Scope scope) {
}
@Override
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}
最后在resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory中添加你的這個Factory即可。
浙公網(wǎng)安備 33010602011771號