使用場景 RequiredActionProvider,它是在認證過程中,需要當前登錄的用戶執行個性化的動作;當用戶符合條件,就被執行RequiredActionProvider對作,當RequiredActionProvider沒有正常提交(context.success())之前,當前用戶仍然是 ...
使用場景
RequiredActionProvider,它是在認證過程中,需要當前登錄的用戶執行個性化的動作;當用戶符合條件,就被執行RequiredActionProvider對作,當RequiredActionProvider沒有正常提交(context.success()
)之前,當前用戶仍然是未登錄
狀態,這在keycloak框架中,也有一些預設的個性化動作,它與整個登錄流程是解耦的,事實上,keycloak的設計理念也是微架構設計,插件化設計。
keycloak預設提供的RequiredActionProvider
- VERIFY_EMAIL 驗證郵箱
- UPDATE_PROFILE 更新用戶信息
- CONFIGURE_TOTP 配置totp多因數認證
- UPDATE_PASSWORD 強制更新密碼,用在臨時建立的密碼場景(CredentialRepresentation中的isTemporary為true時執行)
- TERMS_AND_CONDITIONS 用戶在首次登錄時會被要求查看並接受特定的服務條款和條件
- VERIFY_PROFILE 驗證個人信息
keycloak後臺配置RequiredActionProvider
在側-驗證菜單,選擇Required Action標簽,可以管理它們,開啟或者設置成預設,同時也可以添加自定義的RequiredActionProvider
1 配置列表
2 添加新的Required Action
自定義的RequiredActionProvider
下麵我們添加一個自定義的RequiredActionProvider,業務場景是,當登錄用戶名首碼是test時,就讓這個用戶去驗證手機號
1 添加一個UpdatePhoneNumberRequiredAction
文件,讓它實現RequiredActionProvider介面
public class UpdatePhoneNumberRequiredAction implements RequiredActionProvider {
public static final String PROVIDER_ID = "UPDATE_PHONE_NUMBER";
@Override
public void evaluateTriggers(RequiredActionContext context) {
}
@Override
public void requiredActionChallenge(RequiredActionContext context) {
Response challenge = context.form()
.createForm("login-update-phone-number.ftl");
context.challenge(challenge);
}
@Override
public void processAction(RequiredActionContext context) {
TokenCodeServiceProvider tokenCodeServiceProvider = context.getSession().getProvider(TokenCodeServiceProvider.class);
String phoneNumber = context.getHttpRequest().getDecodedFormParameters().getFirst("phoneNumber");
String code = context.getHttpRequest().getDecodedFormParameters().getFirst("code");
try {
tokenCodeServiceProvider.validateCode(context.getUser(), phoneNumber, code);
context.success();
} catch (BadRequestException e) {
Response challenge = context.form()
.setError("noOngoingVerificationProcess")
.createForm("login-update-phone-number.ftl");
context.challenge(challenge);
} catch (ForbiddenException e) {
Response challenge = context.form()
.setAttribute("phoneNumber", phoneNumber)
.setError("verificationCodeDoesNotMatch")
.createForm("login-update-phone-number.ftl");
context.challenge(challenge);
}
}
@Override
public void close() {
}
}
2 添加UpdatePhoneNumberRequiredActionFactory文件,讓它去構建上面的UpdatePhoneNumberRequiredAction實例
public class UpdatePhoneNumberRequiredActionFactory implements RequiredActionFactory {
private static final UpdatePhoneNumberRequiredAction instance = new UpdatePhoneNumberRequiredAction();
@Override
public String getDisplayText() {
return "";
}
@Override
public RequiredActionProvider create(KeycloakSession session) {
return instance;
}
@Override
public void init(Scope scope) {
}
@Override
public void postInit(KeycloakSessionFactory sessionFactory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return UpdatePhoneNumberRequiredAction.PROVIDER_ID;
}
}
3 在resources/META-INF/services/文件夾下,添加org.keycloak.authentication.RequiredActionFactory文件,通過SPI的方式,註冊咱們的UpdatePhoneNumberRequiredActionFactory工廠
org.keycloak.phone.authentication.requiredactions.UpdatePhoneNumberRequiredActionFactory
4 添加咱們這個UpdatePhoneNumberRequiredActionFactory,它在keycloak後臺RequiredActionProvider中,顯示的名稱是“Update Phone Number”,我們去添加並開啟它
5 在brower的認證流程中,你需要在context.success()之前去判斷用戶名的首碼,併為它指定RequiredAction,如果是對所有用戶有效的,那不需要添加以下代碼,可以把它在keycloak後臺,設置為“預設”行為即可。
- 要想使RequiredAction生效,需要先在keycloak後臺啟用它
- 如果希望對新建用戶啟用它,需要先在keycloak後臺啟用它,並開啟“預設”選項【預設是對新用戶來說的,老用戶不受這個值的控制,就是說你新建一個requiredAction,沒有任何規則,如果你開啟+預設,那它只對所有新建用戶有效】
- 要想對
某些規則的用戶
啟用它,需要在form表單認證時,添加對應的業務邏輯,可以添加自定義的"Authenticator"來實現這個邏輯,儘量不修改之前的核心代碼,註意,如果你在後臺開啟了預設
選項,那對於新用戶也會進入的,不會受規則的約束;為瞭解決這個問題,我們在添加規則時,對不符合的用戶應該添加context.getUser().removeRequiredAction
的邏輯代碼。 - 當前用戶執行的RequiredAction步驟,會在數據表
user_required_action
中存儲,用戶每次登錄都會檢查這個表的狀態,如果用戶存在這表裡,你的對應的RequiredAction關閉了,事實上,當前這個老用戶在登錄時依然會走這個RequiredAction邏輯。 - 註意,這個
user_required_action
表產生的數據會有緩存,只刪除數據表記錄是不起作用的,需要重啟keycloak - 這個
user_required_action
表裡對應的用戶數據,當用戶成功驗證後,這條數據會被刪除,下次用戶再登錄,就不會出現required_action了
if(context.getUser().getUsername().startsWith("test")){
context.getUser().addRequiredAction(UpdatePhoneNumberRequiredAction.PROVIDER_ID);
}else{ // 避免開啟預設行為時,新用戶受到影響
context.getUser().removeRequiredAction(UpdatePhoneNumberRequiredAction.PROVIDER_ID);
}
context.success();
整個RequiredAction配置和執行的流程
好了,到目前來說,咱們用戶名登錄時,首碼為test的用戶,都會走這個手機驗證的界面了,在驗證成功前,用戶是不能直接登錄的。
作者:倉儲大叔,張占嶺,
榮譽:微軟MVP
QQ:853066980
支付寶掃一掃,為大叔打賞!