keycloak~登錄時將請求頭(tou)里某(mou)個屬性放入UserSessionModel
UserSessionModel做為用(yong)(yong)戶登錄過程中的(de)(de)一個會(hui)話,可以(yi)(yi)用(yong)(yong)來跨(kua)flow使用(yong)(yong)數(shu)據,這(zhe)些數(shu)據被保存(cun)到內(nei)存(cun)里,在認證(zheng)過程中可以(yi)(yi)被使用(yong)(yong),今天的(de)(de)一個需求(qiu)(qiu)要求(qiu)(qiu)在登錄時從(cong)請(qing)求(qiu)(qiu)頭獲取(qu)IP所(suo)在地并(bing)寫到kafka里,要想實(shi)現(xian)這(zhe)個需求(qiu)(qiu),你可以(yi)(yi)在現(xian)有認證(zheng)流程中修改代碼,但不建議(yi)這(zhe)樣做,因為這(zhe)種修改對原始邏輯會(hui)有破壞,keycloak提(ti)供了自定義認證(zheng)流,并(bing)在后臺(tai)可以(yi)(yi)靈活的(de)(de)配(pei)置(zhi)。

相關keycloak中的知識
認證流程的執行動作
從上(shang)面(mian)圖中可以(yi)看到,這個(ge)登錄的(de)(de)過程(cheng)會經歷多個(ge)認(ren)(ren)證(zheng)流(liu)(liu),在所有被開(kai)啟(qi)的(de)(de)認(ren)(ren)證(zheng)流(liu)(liu)執行完成(cheng)后才算登錄成(cheng)功,而這些流(liu)(liu)程(cheng)我們是可以(yi)進行按需開(kai)發并配置的(de)(de),下面(mian)說一(yi)下keycloak認(ren)(ren)證(zheng)過程(cheng)的(de)(de)幾大事(shi)件,以(yi)表(biao)單登錄為例(社區三方認(ren)(ren)證(zheng)流(liu)(liu)程(cheng)更(geng)復(fu)雜一(yi)些:
- 表單提交
- 標準用戶密碼認證流執行
- 擴展認證流執行
- 會話限制 User Session Count Limiter
- 請求頭到session的轉換 Header-session-authenticator
- 黑名單控制 BlackListFilterAuthenticator
- 用戶有效性控制 User Validate
- 弱密碼提醒 Config Simple Password Alert Form
- MFA多因子認證 OTP Form
- 執行jwt token構建流程,包含自定義的
AbstractOIDCProtocolMapper等 - 發布Login登錄成功事件
- 訂閱了Login事件的監聽器可以寫入kafka消息
keycloak認證流程相關元素
- 瀏覽器認證流Browser Flow 繼承AbstractUsernameFormAuthenticator類
- 直接認證流Direct Grant Flow 繼承BaseDirectGrantAuthenticator類
- 用戶所需要動作Require Action 實現RequiredActionProvider接口
- 表單頁面Form Action,實現了FormAction接口
關于登錄事件Login擴展的分析
- 在AbstractUsernameFormAuthenticator實現類中,添加登錄擴展參數
- 定義一個認證流,專業處理這塊請求頭參數,并把它放入登錄事件
- 定義一個Login事件監聽器,在收到后信息后,可進行中間件轉發
- 如果需要擴展token中的屬性,也可以通過擴展認證流來實現,將數據存入userSession,并且在AbstractOIDCProtocolMapper階段讀取userSession的內容寫入到jwtToken中
實現步驟
下(xia)面自定義一個(ge)從請求(qiu)頭獲取屬性寫入userSessionModel的(de)例子
@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中添加你的(de)這個(ge)Factory即可(ke)。