Keycloak 用户存储 SPI 实现
Keycloak User Storage SPI Implementation
我正在尝试实现自定义 keycloack Authenticator SPI 以针对外部数据源进行身份验证。 Spring boot Rest Service 也可以用,我也可以用。
我要解决的用例是
User is presented keycloak login screen. Onsubmission User is validated against external Datasource.
Retrieve some attributes from external datasource, map it to keycloak's id and access token.
Also put in a condition of user restriction of same user logging in multiple times at the same time.
I was thinking, it could be solved by retrieving user session information that's available in the keycloak datasource. If i use external datasource, does keycloak still maintain session information?
我遵循了官方指南(https://www.keycloak.org/docs/latest/server_development/index.html#_auth_spi_walkthrough)的第 8.3 节,这与我需要的非常相似。
现在我跳过并按照第 11 节开始(https://www.keycloak.org/docs/latest/server_development/index.html#_user-storage-spi)似乎也更合适。
我所做的是从实施自定义身份验证器 SPI 开始,认为这不是正确的方法,现在实施了 UserStorageProvider。
/***
* From UserLookupProvider
*/
public UserModel getUserById(String id, RealmModel realm) {
System.out.println("ID: " + id + ":REALM:" + realm);
StorageId storageId = new StorageId(id);
/**
* StorageId.getExternalId() method is invoked to obtain
* the username embeded in the id parameter
*/
String username = storageId.getExternalId();
System.out.println("Name:" + username);
return getUserByUsername(username, realm);
}
/***
* From UserLookupProvider
* This method is invoked by the Keycloak login page when a user logs in
*/
public UserModel getUserByUsername(String username, RealmModel realm) {
System.out.println("USERNAME: " + username + ":REALM:" + realm);
UserModel userModel = loadedUsers.get(username);
if (userModel == null) {
String password = properties.getProperty(username);
if (password != null) {
userModel = createUserModel(realm, username);
System.out.println("New UserModel:");
System.out.println(userModel.toString());
loadedUsers.put(username, userModel);
}
}
return userModel;
}
protected UserModel createUserModel(RealmModel realm, String username) {
return new AbstractUserAdapter(session, realm, model) {
@Override
public String getUsername() {
return username;
}
};
}
已关注文档(https://www.keycloak.org/docs/latest/server_development/index.html#packaging-and-deployment-2)
The class files for our provider implementation should be placed in a jar. You also have to declare the provider factory class within the META-INF/services/org.keycloak.storage.UserStorageProviderFactory file.
这里的问题是:我创建的jar在"META-INF"文件夹中没有services目录,我需要手动创建并添加吗?
org.keycloak.examples.federation.properties.FilePropertiesStorageFactory
Once you create the jar you can deploy it using regular WildFly means: copy the jar into the deploy/ directory or using the JBoss CLI.
使用 maven 创建 jar 后,将 jar 复制到 "keycloak-6.0.1\standalone\deployments" 文件夹。但我没有在 "User Federation list"
中看到我的提供商
任何 suggestion/help 将不胜感激!!
提前感谢您的建议。
万一有人 运行 遇到这样的问题:
UserStorage SPI 未显示,因为 META-INF/services 文件夹。文档中有提供,但不清楚
在src/main/resources中创建文件夹结构META-INF/services
在 META-INF/services 目录中创建一个名为 org.keycloak.storage.UserStorageProviderFactory 的文件(整个就是文件名)。其内容是您的 SPI 的完全限定 class 名称:
com.test.UserSpi
我不太确定你需要什么。让我们从区分 Authentication SPI(联合身份检查)和 User Provider SPI(联合用户)开始。第一个(文档的第 8 节 - 更多地关注针对外部服务对用户进行身份验证 - 类似于 facebook,或 google)。联合用户存储更像是您在遗留系统中拥有自己的用户及其遗留 "roles structure",并且您基本上想通过 keycloak 管理它们(通过导入它们,或者通过某些 API 查询) - 这将是该文档的第 11 节)。所以请确定您确实需要什么。
第二,您提到以下内容:
> User is presented keycloak login screen. Onsubmission User is
> validated against external Datasource.
>
> Retrieve some attributes from external datasource, map it to
> keycloak's id and access token.
>
> Also put in a condition of user restriction of same user logging in
> multiple times at the same time.
>
> I was thinking, it could be solved by retrieving user session
> information that's available in the keycloak datasource. If i use
> external datasource, does keycloak still maintain session information?
你的意思是:从外部数据源检索一些属性,将其映射到 keycloak 的 ID 和访问令牌。?通常您只检索用户核心信息,可能还有角色和其他自定义属性(不是会话信息)。 Keycloak 本身作为基于 openIDConnect 的授权服务器,将生成访问令牌,其中已经包含有关哪些受保护资源可以被谁访问的信息,因此您实际上不需要从其他地方导入任何会话,也不必担心上述令牌的生成.
关于:也加入了同用户登录的用户限制条件
同时多次。 当您第一次登录时,您到底想完成(或避免什么?)您的客户端收到一个有效期为 X 的 Bearer 令牌,在此期间您不需要再次登录的时间,直到令牌过期或被删除;再次是您的 Auth 服务器负责的事情,而不是您实现的事情。您有更具体的要求吗?
我在想,这可以通过检索 keycloak 数据源中可用的用户会话信息来解决。如果我使用外部数据源,keycloak 是否仍然维护会话信息? 这听起来不对,你指的是什么会话数据?或者你需要访问?您的用户数据、范围、角色等可以通过 KEycloak Rest API (https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_overview) 访问。您的外部数据源用于用户相关的核心数据(不是外部会话),您认为为什么需要导入外部会话?
好的,您已说明您需要一个用户存储提供程序 API。很棒
现在你的第二个 "Problem/Challenge":
从外部数据源中检索一些属性,将其映射到 keycloak 的 ID 和访问令牌。需要检索用户唯一 ID 并将其作为主题 ID 添加到 jwt 中。这是 id,当此令牌传递给其他服务时,其余服务可用于检索 id。
为此,您能做的最好的事情是:
将这些用户的唯一数据添加为用户属性(在管理控制台上查看)
在 Keycloak 上创建一个 "Client scope",使用 "user property" 的映射器
将您想要(从您的用户)添加的那些属性映射到您的 Id-token 和 access-token。您还需要将您的客户端与您刚刚创建的 "client scope" 联系起来。这可能听起来有点令人困惑,但这个视频很棒 material 我相信它会对你有很大帮助:https://www.youtube.com/watch?v=ZxpY_zZ52kU(大约 6:30 你会看到如何添加额外的用户您的代币信息)
另请查看此页面:https://jwt.io/(当您将编码的令牌粘贴到此处时,您可以看到它们的内容),它是开发人员的绝佳工具。
当你推进你的解决方案时,我会帮助解决独特的问题,或者你post那是一个不同的问题,因为那是一个不同的问题。
希望对您有所帮助。
我正在尝试实现自定义 keycloack Authenticator SPI 以针对外部数据源进行身份验证。 Spring boot Rest Service 也可以用,我也可以用。
我要解决的用例是
User is presented keycloak login screen. Onsubmission User is validated against external Datasource.
Retrieve some attributes from external datasource, map it to keycloak's id and access token.
Also put in a condition of user restriction of same user logging in multiple times at the same time.
I was thinking, it could be solved by retrieving user session information that's available in the keycloak datasource. If i use external datasource, does keycloak still maintain session information?
我遵循了官方指南(https://www.keycloak.org/docs/latest/server_development/index.html#_auth_spi_walkthrough)的第 8.3 节,这与我需要的非常相似。
现在我跳过并按照第 11 节开始(https://www.keycloak.org/docs/latest/server_development/index.html#_user-storage-spi)似乎也更合适。
我所做的是从实施自定义身份验证器 SPI 开始,认为这不是正确的方法,现在实施了 UserStorageProvider。
/***
* From UserLookupProvider
*/
public UserModel getUserById(String id, RealmModel realm) {
System.out.println("ID: " + id + ":REALM:" + realm);
StorageId storageId = new StorageId(id);
/**
* StorageId.getExternalId() method is invoked to obtain
* the username embeded in the id parameter
*/
String username = storageId.getExternalId();
System.out.println("Name:" + username);
return getUserByUsername(username, realm);
}
/***
* From UserLookupProvider
* This method is invoked by the Keycloak login page when a user logs in
*/
public UserModel getUserByUsername(String username, RealmModel realm) {
System.out.println("USERNAME: " + username + ":REALM:" + realm);
UserModel userModel = loadedUsers.get(username);
if (userModel == null) {
String password = properties.getProperty(username);
if (password != null) {
userModel = createUserModel(realm, username);
System.out.println("New UserModel:");
System.out.println(userModel.toString());
loadedUsers.put(username, userModel);
}
}
return userModel;
}
protected UserModel createUserModel(RealmModel realm, String username) {
return new AbstractUserAdapter(session, realm, model) {
@Override
public String getUsername() {
return username;
}
};
}
已关注文档(https://www.keycloak.org/docs/latest/server_development/index.html#packaging-and-deployment-2)
The class files for our provider implementation should be placed in a jar. You also have to declare the provider factory class within the META-INF/services/org.keycloak.storage.UserStorageProviderFactory file.
这里的问题是:我创建的jar在"META-INF"文件夹中没有services目录,我需要手动创建并添加吗?
org.keycloak.examples.federation.properties.FilePropertiesStorageFactory Once you create the jar you can deploy it using regular WildFly means: copy the jar into the deploy/ directory or using the JBoss CLI.
使用 maven 创建 jar 后,将 jar 复制到 "keycloak-6.0.1\standalone\deployments" 文件夹。但我没有在 "User Federation list"
任何 suggestion/help 将不胜感激!!
提前感谢您的建议。
万一有人 运行 遇到这样的问题:
UserStorage SPI 未显示,因为 META-INF/services 文件夹。文档中有提供,但不清楚
在src/main/resources中创建文件夹结构META-INF/services
在 META-INF/services 目录中创建一个名为 org.keycloak.storage.UserStorageProviderFactory 的文件(整个就是文件名)。其内容是您的 SPI 的完全限定 class 名称: com.test.UserSpi
我不太确定你需要什么。让我们从区分 Authentication SPI(联合身份检查)和 User Provider SPI(联合用户)开始。第一个(文档的第 8 节 - 更多地关注针对外部服务对用户进行身份验证 - 类似于 facebook,或 google)。联合用户存储更像是您在遗留系统中拥有自己的用户及其遗留 "roles structure",并且您基本上想通过 keycloak 管理它们(通过导入它们,或者通过某些 API 查询) - 这将是该文档的第 11 节)。所以请确定您确实需要什么。
第二,您提到以下内容:
> User is presented keycloak login screen. Onsubmission User is
> validated against external Datasource.
>
> Retrieve some attributes from external datasource, map it to
> keycloak's id and access token.
>
> Also put in a condition of user restriction of same user logging in
> multiple times at the same time.
>
> I was thinking, it could be solved by retrieving user session
> information that's available in the keycloak datasource. If i use
> external datasource, does keycloak still maintain session information?
你的意思是:从外部数据源检索一些属性,将其映射到 keycloak 的 ID 和访问令牌。?通常您只检索用户核心信息,可能还有角色和其他自定义属性(不是会话信息)。 Keycloak 本身作为基于 openIDConnect 的授权服务器,将生成访问令牌,其中已经包含有关哪些受保护资源可以被谁访问的信息,因此您实际上不需要从其他地方导入任何会话,也不必担心上述令牌的生成.
关于:也加入了同用户登录的用户限制条件 同时多次。 当您第一次登录时,您到底想完成(或避免什么?)您的客户端收到一个有效期为 X 的 Bearer 令牌,在此期间您不需要再次登录的时间,直到令牌过期或被删除;再次是您的 Auth 服务器负责的事情,而不是您实现的事情。您有更具体的要求吗?
我在想,这可以通过检索 keycloak 数据源中可用的用户会话信息来解决。如果我使用外部数据源,keycloak 是否仍然维护会话信息? 这听起来不对,你指的是什么会话数据?或者你需要访问?您的用户数据、范围、角色等可以通过 KEycloak Rest API (https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_overview) 访问。您的外部数据源用于用户相关的核心数据(不是外部会话),您认为为什么需要导入外部会话?
好的,您已说明您需要一个用户存储提供程序 API。很棒
现在你的第二个 "Problem/Challenge":
从外部数据源中检索一些属性,将其映射到 keycloak 的 ID 和访问令牌。需要检索用户唯一 ID 并将其作为主题 ID 添加到 jwt 中。这是 id,当此令牌传递给其他服务时,其余服务可用于检索 id。
为此,您能做的最好的事情是:
将这些用户的唯一数据添加为用户属性(在管理控制台上查看)
在 Keycloak 上创建一个 "Client scope",使用 "user property" 的映射器 将您想要(从您的用户)添加的那些属性映射到您的 Id-token 和 access-token。您还需要将您的客户端与您刚刚创建的 "client scope" 联系起来。这可能听起来有点令人困惑,但这个视频很棒 material 我相信它会对你有很大帮助:https://www.youtube.com/watch?v=ZxpY_zZ52kU(大约 6:30 你会看到如何添加额外的用户您的代币信息)
另请查看此页面:https://jwt.io/(当您将编码的令牌粘贴到此处时,您可以看到它们的内容),它是开发人员的绝佳工具。
当你推进你的解决方案时,我会帮助解决独特的问题,或者你post那是一个不同的问题,因为那是一个不同的问题。
希望对您有所帮助。