如何在不创建账户的情况下,将自定义邮件模板发送到某个邮箱地址?
我在Keycloak 26中实现一个自定义的 EventListenerProvider。
当发生 RESET_PASSWORD_ERROR 事件时,我想把通知邮件发送到输入的邮箱地址。
关键点是:
- 邮箱地址可能不存在于Keycloak的数据库中,
- 我不想在Keycloak中创建一个真实的用户,
- 我想呈现并发送一个自定义的
FreeMarker邮件模板。
我已经能够从事件详情中提取邮箱地址,并且可以用 DefaultEmailSenderProvider 发送一封普通邮件。然而,我想使用来自我的邮箱主题的自定义邮件模板。
我当前的尝试使用 FreeMarkerProvider.processTemplate(...),但我很困难,因为大多数示例和提供者似乎都期望一个UserModel,而我只有一个邮箱地址。
在数据库中没有用户时,如何在Keycloak事件监听器中正确渲染并发送自定义邮件模板?如果答案是 EmailTemplateProvider 需要一个用户,是否有一种干净的方式在不创建持久化用户的情况下使用 FreeMarker 模板?
解决方案
你应该使用 EmailTemplateProvider。这只支持以用户对象作为收件人,因此你必须从一个Keycloak域(realm)获取收件人。你本来不想添加一个用户,但由于这可以在任何域中进行,你可以将用户添加到一个域中,用户不可见。 不幸的是,EmailTemplateProvider 只公开使用内部User-Object集的 send() 方法。
例如,像下面这样的实现:
private void sendLoginFailureEmail(Event event, UserModel user, String recipientRealmName, String recipientEmail, EventType eventType, RealmModel realm) throws EmailException {
setUserTimeStamp(user, "lastLoginFailureTimestamp");
int loginFailureCount = getUserLoginFailureCount(user);
setUserLoginFailureCount(user, loginFailureCount + 1);
if (loginFailureCount > 5) {
RealmModel recipientRealm = keycloakSession.realms().getRealmByName(recipientRealmName);
UserModel recipient = keycloakSession.users().getUserByEmail(recipientRealm, recipientEmail);
Map<String, Object> attributes = new HashMap<>();
attributes.put("event", new EventBean(event));
EmailTemplateProvider emailTemplateProvider = keycloakSession.getProvider(EmailTemplateProvider.class);
if (recipient != null) {
emailTemplateProvider.setRealm(realm).setUser(recipient).send("loginErrorSubject", "login-error.ftl", attributes);
}
}
EventBean用于向模板提供事件信息。这个示例来自Keycloak的源代码:
public class EventBean {
private final Event event;
public EventBean(Event event) {
this.event = event;
}
public Date getDate() {
return new Date(event.getTime());
}
public String getEvent() {
return event.getType().toString().toLowerCase().replace("_", " ");
}
public String getClient() {
return event.getClientId();
}
public String getIpAddress() {
return event.getIpAddress();
}
...
如果使用现有用户绝对不是选项,你可以实现 UserModel。例如,如下所示:
public class EMailUser implements UserModel {
private String email;
private final String id;
public EMailUser(String email) {
this.email = email;
this.id = UUID.randomUUID().toString();
}
@Override
public String getId() {
return this.id;
}
@Override
public String getEmail() {
return this.email;
}
@Override
public void setEmail(String email) {
this.email = email;
}
// other methods
}
// use it
EMailUser eMailUser = new EMailUser("[email protected]");
emailTemplateProvider.setRealm(realm).setUser(eMailUser).send("Repeated login Failures for User " + user.getEmail(), "login-error.ftl", attributes);
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。