KeyCloak通过许多行业标准的提供商,主题和连接器进行了预配置。这些内置功能相当强大,但可能并不总是足够的。为了实现自定义功能,KeyCloak允许您使用许多服务提供商接口(SPI)。通过扩展它们,您可以添加一个新的社会身份提供者或做出独特的表格操作来判断人类和自动化访问之间的区别。
关键概念
有两个基本组件构成了自定义的KeyCloak服务提供商:ProviderFactory
和实际的Provider
本身。
ProviderFactory
所隐含的名称,ProviderFactory
是负责实际创建Provider
的组件。一旦启动了KeyCloak服务器,就会发现该工厂,并且在服务器操作的过程中只有一个实例。它的界面仅限于基本位,如下:
public interface ProviderFactory<T extends Provider> {
void init(Config.Scope config);
void postInit(KeycloakSessionFactory factory);
void close();
T create(KeycloakSession session);
// not mentioned: getId, order, getConfigMetadata
}
现在,让我们更深入地检查每种方法。
void init(config.scope config)
在工厂初始化期间,init
将被调用一次。配置将从keycloak_server.json
文件中获得。您可以使用此方法来初始化和创建将在提供商实现中使用的对象。为了说明,HTTP客户端和数据库客户端是两个几乎不应超过一次的对象的示例。
void PostInit(KeyCloaksessionFactory Factory)
在所有提供商的工厂都已初始化后,将调用postInit
功能。现在,您可以访问所需的任何其他ProviderFactories
和SPI。此外,如果您的业务逻辑要求它,您也可以初始化重复出现的任务。
void Close()
服务器关闭时,close
方法将调用。在init
或postInit
方法中创建的资源,例如HTTP客户端。
t创建(KeyCloaksession会话)
这是创建实际提供商实例的方法。 KeyCloak在每个请求上都调用此方法,因此提供商对象本身应轻量重量。
提供者
每次提出请求时,KeyCloak都会在ProviderFactory
上调用create
函数以生成新的提供商对象。在提供商中,您将实现要添加到系统的功能的业务逻辑。接口的唯一真正必不可少的部分是close
方法,它可以释放为请求分配但不再使用的任何资源。其他方法将取决于您要实现的实际提供商界面。
提供者辨别
要告诉KeyCloak在哪里可以找到ProviderFactory
,您必须在META-INF/services
中设置一个文件,以特定服务提供商实现命名和服务ProviderFactory
的完全合格名称为文件内容。
反国立
涵盖了自定义提供商的外观基本面,我们现在可以检查在开发KeyCloak扩展时可能出现的不良实践。
AP#1-同一类中实施ProviderFactory和提供商
请查看以下示例,其中ProviderFactory
和Provider
在同一类中实现。
class AntiPatternProvider implements ProviderFactory<AntiPatternProvider>, Provider {
private HTTPClient httpClient;
@Override
public AntiPatternProvider create(KeycloakSession session) {
httpClient.post("https://example.com");
return this;
}
@Override
public void init() {
httpClient = new HTTPClient();
}
@Override
public void close() {
httpClient.close()
}
// other implementation specific functions...
}
是的,这看起来很奇怪,但是从理论上讲,这是合法的提供商。您能确定任何潜在问题吗?
服务器将启动而没有任何问题,但是一旦提供商收到第二个请求,就会说明HTTP客户端已关闭的错误消息将出现。 KeyCloak不知道我们只想在服务器关闭时关闭HTTP客户端。相反,每个请求将导致客户端关闭。这是因为我们正在实施ProviderFactory
和Provider
的close
方法,正如我们所看到的那样,它们负有不同的责任。
我们可以通过在单独的类中实现Provider
和ProviderFactory
来避免问题。
class BestPracticeProvider implements Provider {
private final HTTPClient httpClient;
public BestPracticeProvider(HTTPClient httpClient) {
this.httpClient = httpClient;
}
@Override
pubic void close() {
// This method will be called on each request.
// Do not close the HTTP client here!
}
}
class BestPracticeProviderFactory implements ProviderFactory<BestPracticeProvider> {
private HTTPClient httpClient;
@Override
public BestPracticeProvider create(KeycloakSession session) {
return new BestPracticeProvider(httpClient);
}
@Override
public void init() {
httpClient = new HTTPClient();
}
@Override
public void close() {
// The server is shutting down.
// We can safely close the HTTP client.
httpClient.close()
}
// other implementation specific functions...
}
AP#2-请求期间创建重量重量对象
在下面的示例中,我们将查看创建提供商时可能出现的另一个反图案。
class AntiPatternProvider implements Provider {
private final HTTPClient httpClient;
public AntiPatternProvider() {
httpClient = new HTTPClient();
}
@Override
public void close() {
httpClient.close();
}
}
class AntiPatternProviderFactory implements ProviderFactory<AntiPatternProvider> {
@Override
public AntiPatternProvider create(KeycloakSession session) {
return new AntiPatternProvider();
}
// other implementation specific functions...
}
在此提供商实施中,在每个请求期间创建和关闭HTTP客户端。问题是客户是一个重对象。为每个请求创建它都是昂贵的。与我们修复AP#1的方式类似,我们需要让ProviderFactory
处理HTTP客户端的创建和关闭。
结论
KeyCloak提供了许多扩展其内置功能的可能性。任何从事扩展的人都应该先熟悉Keycloak SPI。我认为,KeyCloak的文档和示例并不好,尤其是对于刚刚起步的开发人员。
所以,以下是您启动时的一些一般建议:
- 将每个提供商视为一个独立的应用程序。
- 请注意一旦关闭服务器或请求完成后必须发布的资源。否则,您可能会无意间创建内存泄漏。
- 尽可能避免在同一库中有多个提供商。相反,请尝试将它们分开每个域。这将使测试,维护和部署它们更容易。
- 请提供最新的keycloak版本。
- 请勿将外部库与shadowJar打包(仅适用于Quarkus分发)。而是将库添加到
providers
目录。
谢谢您的阅读
您有任何疑问吗?本文是否有帮助?在下面的评论中让我知道。