代码之家  ›  专栏  ›  技术社区  ›  Scott

密钥斗篷无输入授权

  •  0
  • Scott  · 技术社区  · 3 年前

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

    使用 this question 作为一个起点,我已经在KeyClope中创建了自己的自定义身份验证SPI。我还根据需要配置了KeyClope身份验证流。

    自定义验证器

    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;
      }
    
    }
    

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

    <#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>
    

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

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

    0 回复  |  直到 3 年前
        1
  •  0
  •   Scott    3 年前

    问题是我试图在 authenticate() action() .

    另外 MultivaluedMap Context

    下面的解决方案是从 this question

    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");