我已经实现了一个自定义用户存储提供程序,用于从我们的数据库联合用户。
我希望通过keycloak为这些用户管理OTP,当我在流中将OTP设置为required,并将OTP配置为所需的操作时,otp表单将在联合用户登录后显示,但当我试图设置OTP时,接收到的错误用户将仅为此更新读取。
如何允许只读联邦用户允许通过keycloak进行OTP配置?
2022-01-31 17:00:12,704 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (default task-669) Uncaught server error: org.keycloak.storage.ReadOnlyException: user is read only for this update
at org.keycloak.keycloak-server-spi@15.1.1//org.keycloak.storage.adapter.AbstractUserAdapter.removeRequiredAction(AbstractUserAdapter.java:77)
at org.keycloak.keycloak-services@15.1.1//org.keycloak.services.resources.LoginActionsService.processRequireAction(LoginActionsService.java:1044)
at org.keycloak.keycloak-services@15.1.1//org.keycloak.services.resources.LoginActionsService.requiredActionPOST(LoginActionsService.java:967)
用户适配器是
public class UserAdminAdapter extends AbstractUserAdapter {
private final CustomUser user;
public UserAdminAdapter(
KeycloakSession session,
RealmModel realm,
ComponentModel storageProviderModel,
CustomUser user) {
super(session, realm, storageProviderModel);
this.user = user;
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public Stream<String> getAttributeStream(String name) {
Map<String, List<String>> attributes = getAttributes();
return (attributes.containsKey(name)) ? attributes.get(name).stream() : Stream.empty();
}
@Override
protected Set<GroupModel> getGroupsInternal() {
if (user.getGroups() != null) {
return user.getGroups().stream().map(UserGroupModel::new).collect(Collectors.toSet());
}
return new HashSet<>();
}
@Override
protected Set<RoleModel> getRoleMappingsInternal() {
if (user.getRoles() != null) {
return user.getRoles().stream().map(roleName -> new UserRoleModel(roleName, realm)).collect(Collectors.toSet());
}
return new HashSet<>();
}
@Override
public boolean isEnabled() {
return user.isEnabled();
}
@Override
public String getId() {
return StorageId.keycloakId(storageProviderModel, user.getUserId() + "");
}
@Override
public String getFirstAttribute(String name) {
List<String> list = getAttributes().getOrDefault(name, Collections.emptyList());
return list.isEmpty() ? null : list.get(0);
}
@Override
public Map<String, List<String>> getAttributes() {
MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
attributes.add(UserModel.USERNAME, getUsername());
attributes.add(UserModel.EMAIL, getEmail());
attributes.add(UserModel.FIRST_NAME, getFirstName());
attributes.add(UserModel.LAST_NAME, getLastName());
attributes.addAll(user.getAttributes());
return attributes;
}
@Override
public String getFirstName() {
return user.getFirstName();
}
@Override
public String getLastName() {
return user.getLastName();
}
@Override
public String getEmail() {
return user.getEmail();
}
}
发布于 2022-02-06 02:15:56
原因是在您的UserAdminAdapter
类中,您没有实现removeRequiredAction
和addRequiredAction
方法。您正在接收的消息来自基类提供的默认实现。您应该自己实现这些方法,并将所需的操作存储在底层存储中,或者考虑从AbstractUserAdapterFederatedStorage
扩展类,后者将所有这些功能委托给内部Keycloak实现。
发布于 2022-06-02 23:04:31
在我的外部DB中完全支持OTP
最后,在一个多星期后,我开始使用Keycloak 18.0。您需要做什么?简单地说,您必须实现身份验证工作流中的每一个步骤:
SPI
H 212H 113
启用所需action
这是怎么回事?
我们得到一个完全可自定义的OTP身份验证器(领域的策略pending...)
在我看来,这有点烦人,因为我们需要做很多循环才能在本地存储我们的数据,以及如何处理集成的OTP表单(为了“自然的外观”),但是它让我完全控制了OTP的集成,而且,我可以备份我的数据库,并且他们的OTP身份验证仍然存在,所以,如果我在KC升级失败或者它被损坏了,我仍然拥有所有的数据。
最后,下面是当您的经理具有自定义OTP身份验证时应该看到的样子
https://stackoverflow.com/questions/70929788
复制