keycloak~RequiredActionProvider的使用
使用場景
RequiredActionProvider,它是在認(rèn)證過程中,需要當(dāng)前登錄的用戶執(zhí)行個(gè)性化的動(dòng)作;當(dāng)用戶符合條件,就被執(zhí)行RequiredActionProvider對(duì)作,當(dāng)RequiredActionProvider沒有正常提交(context.success())之前,當(dāng)前用戶仍然是未登錄狀態(tài),這在keycloak框架中,也有一些默認(rèn)的個(gè)性化動(dòng)作,它與整個(gè)登錄流程是解耦的,事實(shí)上,keycloak的設(shè)計(jì)理念也是微架構(gòu)設(shè)計(jì),插件化設(shè)計(jì)。
keycloak默認(rèn)提供的RequiredActionProvider
- VERIFY_EMAIL 驗(yàn)證郵箱
- UPDATE_PROFILE 更新用戶信息
- CONFIGURE_TOTP 配置totp多因子認(rèn)證
- UPDATE_PASSWORD 強(qiáng)制更新密碼,用在臨時(shí)建立的密碼場景(CredentialRepresentation中的isTemporary為true時(shí)執(zhí)行)
- TERMS_AND_CONDITIONS 用戶在首次登錄時(shí)會(huì)被要求查看并接受特定的服務(wù)條款和條件
- VERIFY_PROFILE 驗(yàn)證個(gè)人信息
keycloak后臺(tái)配置RequiredActionProvider
在側(cè)-驗(yàn)證菜單,選擇Required Action標(biāo)簽,可以管理它們,開啟或者設(shè)置成默認(rèn),同時(shí)也可以添加自定義的RequiredActionProvider
1 配置列表

2 添加新的Required Action

自定義的RequiredActionProvider
下面我們添加一個(gè)自定義的RequiredActionProvider,業(yè)務(wù)場景是,當(dāng)?shù)卿浻脩裘熬Y是test時(shí),就讓這個(gè)用戶去驗(yàn)證手機(jī)號(hào)
1 添加一個(gè)UpdatePhoneNumberRequiredAction文件,讓它實(shí)現(xiàn)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文件,讓它去構(gòu)建上面的UpdatePhoneNumberRequiredAction實(shí)例
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 添加咱們這個(gè)UpdatePhoneNumberRequiredActionFactory,它在keycloak后臺(tái)RequiredActionProvider中,顯示的名稱是“Update Phone Number”,我們?nèi)ヌ砑硬㈤_啟它

5 在brower的認(rèn)證流程中,你需要在context.success()之前去判斷用戶名的前綴,并為它指定RequiredAction,如果是對(duì)所有用戶有效的,那不需要添加以下代碼,可以把它在keycloak后臺(tái),設(shè)置為“默認(rèn)”行為即可。
- 要想使RequiredAction生效,需要先在keycloak后臺(tái)啟用它
- 如果希望對(duì)新建用戶啟用它,需要先在keycloak后臺(tái)啟用它,并開啟“默認(rèn)”選項(xiàng)【默認(rèn)是對(duì)新用戶來說的,老用戶不受這個(gè)值的控制,就是說你新建一個(gè)requiredAction,沒有任何規(guī)則,如果你開啟+默認(rèn),那它只對(duì)所有新建用戶有效】
- 要想對(duì)
某些規(guī)則的用戶啟用它,需要在form表單認(rèn)證時(shí),添加對(duì)應(yīng)的業(yè)務(wù)邏輯,可以添加自定義的"Authenticator"來實(shí)現(xiàn)這個(gè)邏輯,盡量不修改之前的核心代碼,注意,如果你在后臺(tái)開啟了默認(rèn)選項(xiàng),那對(duì)于新用戶也會(huì)進(jìn)入的,不會(huì)受規(guī)則的約束;為了解決這個(gè)問題,我們在添加規(guī)則時(shí),對(duì)不符合的用戶應(yīng)該添加context.getUser().removeRequiredAction的邏輯代碼。 - 當(dāng)前用戶執(zhí)行的RequiredAction步驟,會(huì)在數(shù)據(jù)表
user_required_action中存儲(chǔ),用戶每次登錄都會(huì)檢查這個(gè)表的狀態(tài),如果用戶存在這表里,你的對(duì)應(yīng)的RequiredAction關(guān)閉了,事實(shí)上,當(dāng)前這個(gè)老用戶在登錄時(shí)依然會(huì)走這個(gè)RequiredAction邏輯。 - 注意,這個(gè)
user_required_action表產(chǎn)生的數(shù)據(jù)會(huì)有緩存,只刪除數(shù)據(jù)表記錄是不起作用的,需要重啟keycloak - 這個(gè)
user_required_action表里對(duì)應(yīng)的用戶數(shù)據(jù),當(dāng)用戶成功驗(yàn)證后,這條數(shù)據(jù)會(huì)被刪除,下次用戶再登錄,就不會(huì)出現(xiàn)required_action了
if(context.getUser().getUsername().startsWith("test")){
context.getUser().addRequiredAction(UpdatePhoneNumberRequiredAction.PROVIDER_ID);
}else{ // 避免開啟默認(rèn)行為時(shí),新用戶受到影響
context.getUser().removeRequiredAction(UpdatePhoneNumberRequiredAction.PROVIDER_ID);
}
context.success();
整個(gè)RequiredAction配置和執(zhí)行的流程

- 定義多個(gè)required action時(shí),它們的排序方式,是keycloak后臺(tái)進(jìn)行設(shè)置,為正序加載執(zhí)行
- 每一個(gè)required action都是一個(gè)單獨(dú)的事件,當(dāng)在事件中執(zhí)行到
context.success()表示執(zhí)行完成,下次用戶再登錄后,不會(huì)再進(jìn)入這個(gè)action(對(duì)應(yīng)的數(shù)據(jù)記錄會(huì)從user_required_action中刪除)
好了,到目前來說,咱們用戶名登錄時(shí),前綴為test的用戶,都會(huì)走這個(gè)手機(jī)驗(yàn)證的界面了,在驗(yàn)證成功前,用戶是不能直接登錄的。
浙公網(wǎng)安備 33010602011771號(hào)