当前位置: 首页 > 知识库问答 >
问题:

密钥斗篷无输入授权

郑俊美
2023-03-14

我正在尝试编写一个定制的KeyClope验证器,该验证器可以从某个请求中检索用户凭据,并动态提交这些凭据进行身份验证,而最终用户不必手动将它们输入某个登录表单。

以这个问题为起点,我在KeyCloak中创建了自己的自定义身份验证SPI。我还根据需要配置了KeyCloak身份验证流。

自定义验证器

import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.authenticators.browser.UsernamePasswordForm;

public class CustomUPForm extends UsernamePasswordForm implements Authenticator {

  @Override
  public void authenticate(AuthenticationFlowContext context) {
    System.out.println("Authenticating....");
    
    Response challenge = context.form().createForm("custom-up-form.ftl");
    context.challenge(challenge);
    
    MultivaluedMap<String, String> formData = new MultivaluedHashMap<>();
    //Changed here - but otherwise valid credentials
    formData.putSingle("username", "xxxxx");
    formData.putSingle("password", "xxxxx");
    context.form().setFormData(formData);
  }

  @Override
  public void action(AuthenticationFlowContext context) {
    System.out.println("Action....");
    context.success();
  }

}

自定义验证器工厂

import java.util.ArrayList;
import java.util.List;
import org.keycloak.Config;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;

public class CustomUPFormFactory implements AuthenticatorFactory,
    ConfigurableAuthenticatorFactory {

  public static final String PROVIDER_ID = "custom-up-form";
  public static final CustomUPForm SINGLETON = new CustomUPForm();

  @Override
  public Authenticator create(KeycloakSession session) {
    return SINGLETON;
  }

  @Override
  public void init(Config.Scope config) {

  }

  @Override
  public void postInit(KeycloakSessionFactory factory) {

  }

  @Override
  public void close() {

  }

  @Override
  public String getId() {
    return PROVIDER_ID;
  }

  @Override
  public String getDisplayType() {
    return "Custom Authenticator";
  }

  @Override
  public String getReferenceCategory() {
    return "Reference Category";
  }

  @Override
  public boolean isConfigurable() {
    return true;
  }

  public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
      AuthenticationExecutionModel.Requirement.REQUIRED
  };

  @Override
  public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
    return REQUIREMENT_CHOICES;
  }

  @Override
  public String getHelpText() {
    return "POC Custom Authenticator";
  }

  private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = new ArrayList<>();

  static {
    /*
    Add properties here
    */
  }

  @Override
  public List<ProviderConfigProperty> getConfigProperties() {
    return CONFIG_PROPERTIES;
  }

  @Override
  public boolean isUserSetupAllowed() {
    return false;
  }

}

下面是我的自定义登录表单,它基于这里的KeyClope“secret question”SPI示例中提供的表单

<#import "template.ftl" as layout>
<@layout.registrationLayout; section>
    <#if section = "title">
        ${msg("loginTitle",realm.name)}
    <#elseif section = "header">
        ${msg("loginTitleHtml",realm.name)}
    <#elseif section = "form">
        <form id="kc-totp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
            <div class="${properties.kcFormGroupClass!}">
                <div class="${properties.kcLabelWrapperClass!}">
                    <label for="totp" class="${properties.kcLabelClass!}">Login</label>
                </div>

                <div class="${properties.kcInputWrapperClass!}">
                    <input id="totp" name="username" type="text" class="${properties.kcInputClass!}" />
                    <input id="totp" name="password" type="password" class="${properties.kcInputClass!}" />
                </div>
            </div>

            <div class="${properties.kcFormGroupClass!}">
                <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
                    <div class="${properties.kcFormOptionsWrapperClass!}">
                    </div>
                </div>

                <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
                    <div class="${properties.kcFormButtonsWrapperClass!}">
                        <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
                        name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
                    </div>
                </div>
            </div>
        </form>
    </#if>
</@layout.registrationLayout>

所有组件都呈现良好,但是当我动态尝试通过将凭据添加到表单数据来传递凭据时,我总是得到“无效的用户名/密码”响应。

如何在不需要用户手动输入的情况下传递用户名和密码组合?

共有1个答案

朱欣荣
2023-03-14

问题是我试图在authenticate()的范围内执行此操作,而它应该包含在action()的逻辑中。

此外,包含表单数据的多值map应该从当前上下文中包含的HTTP请求派生而来

下面的解决方案就是从这个问题中推导出来的

public class CustomUPForm extends UsernamePasswordForm implements Authenticator {

  @Override
  public void authenticate(AuthenticationFlowContext context) {
    Response challenge = context.form()
        .createForm("custom-up-form.ftl");
    context.challenge(challenge);
  }

  @Override
  public void action(AuthenticationFlowContext context) {
    System.out.println("Processing form...");

    MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();

    formData.putSingle("username", "xxxxx");
    formData.putSingle("password", "xxxxx");

    if (!validateForm(context, formData)) {
      return;
    }

    context.success();
  }
}

请注意,在这个示例中validateForm()包含一些自定义验证逻辑,这些逻辑对于本问题的范围来说并不是必需的。但是用户名和密码的值可以通过调用getFirst()来提取

formData.getFirst("username");

 类似资料:
  • 我是这方面的新手,读了很多之后,我觉得我不太明白如何在Keycloak中实现授权代码流。我的疑惑: > < li> 在创建了支持此流程的客户端后,如何执行凭据验证?默认情况下,如果我不做任何配置,我会得到一个登录表单。如果我在浏览器中打开这个html,并填写用户和密码字段,当我按下按钮时,它会将我发送到一个类型为.../realm/{ REAL _ NAME }/log in-actions/au

  • 我试图使用Keycloak的openId-connectendpoint来获取关于用户角色的信息。我使用/auth/realms/moje/protocol/OpenID-connect/userinfoendpoint来获取关于已验证用户的信息。我可以获得姓名、用户名、电子邮件等信息。但是我不能强迫Keyclak给我关于用户角色的信息。 我已经阅读了openID文档,我没有找到任何关于必须获取角

  • 我有一个需要联合到IDP的需求。我过去从未遇到过问题,在这种情况下,我遇到了问题,因为第三方/外部IDP启用并实施了PKCE。 有没有办法联合到启用了PKCE的IDP。基本上换句话说,我应该能够转发/发送code_challenge和code_challenge_method给外部 IDP。我可以在我的 IDP 上启用 PKCE,而不会出现任何问题,并在需要时将相同的标头转发给外部 IDP,但我看

  • 部署在AWS上,我不想隧道到盒子并打开浏览器禁用它。 似乎存在一个配置:,可以放置在文件,但我不确定在哪个对象下。我在“王国”下试过,但一点运气都没有。 我不想在适配器级别禁用它,它需要全局禁用,那么在哪里,或者如何全局禁用ssh/https? 另外,我知道这在生产中是不推荐的。)

  • 我正在使用SAML和外部IdP实现SSO选项。如果我在单击浏览器中的SSO按钮后检查收到的SAML响应,我可以看到所需的身份验证数据(如用户名和电子邮件),因此与IdP的通信工作正常。 然而,Keycloak并不执行登录,并显示给我一个页面,上面写着:“我们很抱歉...登录超时,请重新登录。”,并且没有新用户注册。我的领域中令牌的登录超时设置为30分钟。 查看日志时,我发现了以下错误: 我发现这可

  • 我正在尝试在API网关(Apache APISIX)后面使用KeyClope。 我使用minikube来运行KeyClope和API网关。 网关正常工作,KeyClope也正常工作: 使用KeyClope,我可以使用不同的endpoint(使用发现endpoint(http://127.0.0.1:7070/auth/realms/myrealm/.well-已知/uma2配置),询问访问令牌并进