You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Keycloak 26中无用户时如何用自定义FreeMarker模板发送邮件?

Keycloak 26中无用户时渲染FreeMarker邮件模板并发送的解决方案

核心思路

绕过依赖UserModel的邮件模板提供商,直接使用FreeMarkerProvider手动处理模板,自行构造模板所需的变量数据,再通过EmailSenderProvider发送渲染后的邮件内容,完全无需创建持久化用户。

具体实现步骤

1. 获取所需的Keycloak Provider

在自定义EventListenerProvider中,通过构造函数注入或从KeycloakSession获取FreeMarkerProvider、EmailSenderProvider和当前RealmModel:

private final FreeMarkerProvider freeMarkerProvider;
private final EmailSenderProvider emailSenderProvider;
private final RealmModel realm;

// 构造函数注入(ProviderFactory中通过session获取实例)
public CustomEventListenerProvider(FreeMarkerProvider freeMarkerProvider, EmailSenderProvider emailSenderProvider, RealmModel realm) {
    this.freeMarkerProvider = freeMarkerProvider;
    this.emailSenderProvider = emailSenderProvider;
    this.realm = realm;
}

2. 从事件中提取邮箱地址

从RESET_PASSWORD_ERROR事件的详情中获取目标邮箱:

String targetEmail = event.getDetails().get("email");

3. 构造FreeMarker模板数据模型

自行创建Map存储模板所需变量,无需依赖真实UserModel:

Map<String, Object> templateData = new HashMap<>();
templateData.put("realmName", realm.getName());
templateData.put("targetEmail", targetEmail);
templateData.put("errorReason", event.getDetails().get("error")); // 可选:传递错误原因
// 可添加任意自定义变量,匹配模板中的占位符

4. 渲染FreeMarker模板

使用FreeMarkerProvider处理模板文件,生成邮件主题和正文:

// 模板路径对应自定义主题的email目录,如themes/your-custom-theme/email/
String subjectTemplatePath = "email/reset-password-error-subject.ftl";
String bodyTemplatePath = "email/reset-password-error-body.ftl";

String emailSubject = freeMarkerProvider.processTemplate(subjectTemplatePath, templateData);
String emailBody = freeMarkerProvider.processTemplate(bodyTemplatePath, templateData);

5. 发送邮件

通过EmailSenderProvider构造邮件并发送,无需传入UserModel:

EmailSenderProvider.Email emailMessage = new EmailSenderProvider.Email();
emailMessage.setFrom(realm.getSmtpConfig().getFrom());
emailMessage.setTo(targetEmail);
emailMessage.setSubject(emailSubject);
emailMessage.setHtml(emailBody); // 若为HTML模板,设置此属性
emailMessage.setText(emailBody); // 纯文本 fallback

emailSenderProvider.send(realm, emailMessage);

特殊场景:模板依赖UserModel属性的处理

如果模板中硬编码了user.xxx这类UserModel属性,可以创建一个临时的非持久化UserModel实现,仅返回需要的属性:

UserModel fakeUser = new UserModel() {
    @Override
    public String getEmail() {
        return targetEmail;
    }

    @Override
    public String getUsername() {
        return targetEmail.split("@")[0]; // 用邮箱前缀模拟用户名
    }

    // 其他未用到的方法返回默认值或抛出UnsupportedOperationException
    @Override
    public String getFirstName() {
        return "";
    }

    @Override
    public String getLastName() {
        return "";
    }

    // 省略其余UserModel接口方法的实现
};

将该假用户加入模板数据模型即可:

templateData.put("user", fakeUser);

注意事项

  • 确保自定义模板放在Keycloak主题目录的email子目录下,且Realm已配置使用该主题。
  • 在EventListenerProviderFactory中正确从KeycloakSession获取所需Provider实例:
@Override
public EventListenerProvider create(KeycloakSession session) {
    return new CustomEventListenerProvider(
        session.getProvider(FreeMarkerProvider.class),
        session.getProvider(EmailSenderProvider.class),
        session.getContext().getRealm()
    );
}
  • 检查Realm的SMTP配置,确保邮件发送功能正常。

内容的提问来源于stack exchange,提问作者Tsvetkov Kirill

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 12:17:27