動態

詳情 返回 返回

keycloak~登录时将请求头里某个属性放入UserSessionModel - 動態 詳情

UserSessionModel做為用户登錄過程中的一個會話,可以用來跨flow使用數據,這些數據被保存到內存裏,在認證過程中可以被使用,今天的一個需求要求在登錄時從請求頭獲取IP所在地並寫到kafka裏,要想實現這個需求,你可以在現有認證流程中修改代碼,但不建議這樣做,因為這種修改對原始邏輯會有破壞,keycloak提供了自定義認證流,並在後台可以靈活的配置。

圖片

相關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消息

keycloak認證流程相關元素

  1. 瀏覽器認證流Browser Flow 繼承AbstractUsernameFormAuthenticator類
  2. 直接認證流Direct Grant Flow 繼承BaseDirectGrantAuthenticator類
  3. 用户所需要動作Require Action 實現RequiredActionProvider接口
  4. 表單頁面Form Action,實現了FormAction接口

實現步驟

下面自定義一個從請求頭獲取屬性寫入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即可。

Add a new 評論

Some HTML is okay.