java如何在KeyClope登录页面上实现Recaptcha
我想在KeyClope登录页面(比如注册页面)中实现recaptcha。我用所需的工厂类扩展了UsernamePasswordForm类。我甚至还暗示了行动要求的课程。但我仍然看不到在提供者标签中添加登录。我修改了现有登录名。ftl也是,但运气不好
下面是我试过的
我的验证器类:
public class MyLoginAuthenticator extends UsernamePasswordForm {
@Override
public void action(AuthenticationFlowContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
if (formData.containsKey("cancel")) {
context.cancelLogin();
return;
}
if (!validateForm(context, formData)) {
return;
}
context.success();
}
protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
return validateUserAndPassword(context, formData);
}
@Override
public void authenticate(AuthenticationFlowContext context) {
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
String loginHint = context.getAuthenticationSession().getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
String rememberMeUsername = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders());
if (loginHint != null || rememberMeUsername != null) {
if (loginHint != null) {
formData.add(AuthenticationManager.FORM_USERNAME, loginHint);
} else {
formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
formData.add("rememberMe", "on");
}
}
Response challengeResponse = challenge(context, formData);
context.challenge(challengeResponse);
}
@Override
public boolean requiresUser() {
return false;
}
protected Response challenge(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
LoginFormsProvider forms = context.form();
if (formData.size() > 0) forms.setFormData(formData);
return forms.createLogin();
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
// never called
return true;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
// never called
}
@Override
public void close() {
}
}
我的工厂课程:
public class LoginAuthenticatorFactory extends UsernamePasswordFormFactory {
public static final String G_RECAPTCHA_RESPONSE = "g-recaptcha-response";
public static final String RECAPTCHA_REFERENCE_CATEGORY = "login-recaptcha";
public static final String SITE_KEY = "site.key";
public static final String SITE_SECRET = "secret";
public static final String PROVIDER_ID = "auth-username-password-form-recaptcha";
public static final MyLoginAuthenticator SINGLETON = new MyLoginAuthenticator();
@Override
public String getDisplayType() {
System.out.println("Ranveer Singh getDisplayType ");
return "Login Recaptcha";
}
@Override
public String getReferenceCategory() {
return RECAPTCHA_REFERENCE_CATEGORY;
}
@Override
public Authenticator create(KeycloakSession session) {
return SINGLETON;
}
@Override
public boolean isConfigurable() {
return true;
}
private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED};
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}
public void buildPage(FormContext context, LoginFormsProvider form) {
System.out.println("Ranveer Singh buildPage");
AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
if (captchaConfig == null || captchaConfig.getConfig() == null || captchaConfig.getConfig().get(SITE_KEY) == null || captchaConfig.getConfig().get(SITE_SECRET) == null) {
form.addError(new FormMessage(null, Messages.RECAPTCHA_NOT_CONFIGURED));
return;
}
String siteKey = captchaConfig.getConfig().get(SITE_KEY);
form.setAttribute("recaptchaRequired", true);
form.setAttribute("recaptchaSiteKey", siteKey);
form.addScript("https://www.google.com/recaptcha/api.js");
}
public void validate(ValidationContext context) {
System.out.println("Ranveer Singh validate");
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
List<FormMessage> errors = new ArrayList<>();
boolean success = false;
context.getEvent().detail(Details.REGISTER_METHOD, "form");
String captcha = formData.getFirst(G_RECAPTCHA_RESPONSE);
if (!Validation.isBlank(captcha)) {
AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
String secret = captchaConfig.getConfig().get(SITE_SECRET);
success = validateRecaptcha(context, success, captcha, secret);
}
if (success) {
context.success();
} else {
errors.add(new FormMessage(null, Messages.RECAPTCHA_FAILED));
formData.remove(G_RECAPTCHA_RESPONSE);
context.error(Errors.INVALID_REGISTRATION);
context.validationError(formData, errors);
return;
}
}
protected boolean validateRecaptcha(ValidationContext context, boolean success, String captcha, String secret) {
System.out.println("Ranveer Singh ");
HttpClient httpClient = context.getSession().getProvider(HttpClientProvider.class).getHttpClient();
HttpPost post = new HttpPost("https://www.google.com/recaptcha/api/siteverify");
List<NameValuePair> formparams = new LinkedList<>();
formparams.add(new BasicNameValuePair("secret", secret));
formparams.add(new BasicNameValuePair("response", captcha));
formparams.add(new BasicNameValuePair("remoteip", context.getConnection().getRemoteAddr()));
try {
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
post.setEntity(form);
HttpResponse response = httpClient.execute(post);
InputStream content = response.getEntity().getContent();
try {
Map json = JsonSerialization.readValue(content, Map.class);
Object val = json.get("success");
success = Boolean.TRUE.equals(val);
} finally {
content.close();
}
} catch (Exception e) {
ServicesLogger.LOGGER.recaptchaFailed(e);
}
return success;
}
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override
public void close() {
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String getHelpText() {
return "Adds Google Recaptcha button. Recaptchas verify that the entity that is registering is a human. This can only be used on the internet and must be configured after you add it.";
}
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(SITE_KEY);
property.setLabel("Recaptcha Site Key");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText("Google Recaptcha Site Key");
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(SITE_SECRET);
property.setLabel("Recaptcha Secret");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText("Google Recaptcha Secret");
configProperties.add(property);
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
}
我还缺什么吗?有人能帮我在登录页面上找到recaptcha吗。以前有人这样做过吗?有人可以分享示例代码,以便我可以查看和尝试更多
提前谢谢
# 1 楼答案
您的实现有几个问题。如果您打算使用浏览器登录功能,最好只为Recaptcha创建一个新流。该流将使用UsernamePasswordForm和UsernamePasswordFormFactory,因此需要扩展这两个类
除了扩展这两个类之外,还必须调用UsernamePasswordForm/Factory中的构造函数来实现新的实现。基本上,这个解决方案为您提供用户名+密码+重述密码
在RecaptchaFormFactory中,您不需要来自注册验证码的构建页面,也不需要ValidateCaptcha和validate(您将在action方法中使用它们,并从RecaptchaForm进行身份验证)
工厂中只需要配置和UsernamePasswordFormFactory中完全相同的方法
在RecaptchaForm类中,您将拥有action方法、authenticate方法和validateRecaptcha方法
调用的第一个方法是具有以下结构的身份验证方法:
validateRecaptcha将具有与RegistrationCaptcha相同的结构,action方法只需要将getEvent从Registration更改为AUTH_方法
# 2 楼答案
基于@ghinea-alex的回复,我们在这个{a1}中做了一个工作{}
我们制作了一个maven模块,它也是一个JBoss模块
首先在{}中扩展{},并且在{}中扩展{}
RecaptchaUsernamePasswordForm:
RecaptchaUsernamePasswordFormFactory:
必须有一个
META-INF
,其中必须有一个service\org.keycloak.authentication.AuthenticatorFactory
。它的内容是:而且所有独立部署的jboss模块都必须有一个
jboss-deployment-structure.xml
,它描述了这些模块的依赖关系:在你的
login.ftl
主题中,你应该在<form></form>
中添加以下内容:最后,您应该启用外部源代码
https://google.com
,就像前面提到的keydeposes Recaptcha Documentation中那样如何使用
在本github repo中,我们创建了一个易于使用的maven模块和使用手册
复制回购协议就行了。 你应该已经安装了
java
和maven
。 对于构建,您需要运行mvn clean install
。它将产生jartarget/recaptcha-login.jar
。 为了让它可以在keydove中访问,您应该将这个jar复制到keydovesstandalone/deployment/
目录中。 就这样。 如果你在docker环境中使用它,你应该把它挂载在/opt/jboss/keycloak/standalone/deployment/recaptcha-login.jar
。 例如,在我的docker compose文件中:在你的主题文件中,你应该在你的登录中添加这段代码。ftl模板文件:
您应该在登录模板(
login.ftl
)中的登录<form></form>
中通过它最后,您应该启用外部源代码
https://google.com
,就像前面提到的keydeposes Recaptcha Documentation中那样要在GUI中启用它,请执行以下操作: 转到身份验证
然后为自己创建一个流,在本例中,我的流是
BrowserWithRecaptcha
,它应该类似于KeyCloves的默认流Browser
,只是它有Recaptcha Username Password Form
而不是Username Password Form
:然后根据您的google recaptcha键在以下位置配置
Recaptacha Uusername Password Form
:然后在下一个选项卡中将
Browser Flow
绑定到BrowserWithRecaptcha
:同时,允许谷歌进入也是必须的。com访问
Realm Settings
>Security Defences
# 3 楼答案
这里是另一种方法,可能不像其他伟大的开发人员所建议的那样优雅,但是它可能对那些正在寻找基于主题的解决方案的人有所帮助
themes > base > login.ftl
中添加下面的代码行themes > base > theme.properties
刷新你的页面,清除缓存,你会得到有效的验证码
它不是很强大,因为它只是禁用登录按钮,直到你/用户从谷歌重新验证码服务获得成功回复