Keycloak KeyClope自定义验证器存在问题

Keycloak KeyClope自定义验证器存在问题,keycloak,Keycloak,我正在尝试构建定制的DirectGrantAuthenticator,它将来应该进行网络调用,以根据外部服务对用户/pwd进行身份验证 目前,我有一个虚拟验证器,它应该为每个用户名/pwd对用户进行身份验证,并用默认声明进行响应 在身份验证调用过程中,我遇到了一个KeyClope异常,不知道如何修复它。。看来keydove在会话中仍然看不到authenticate user keycloak_1 | 13:32:10,638 INFO [stdout] (default task-1) qu

我正在尝试构建定制的DirectGrantAuthenticator,它将来应该进行网络调用,以根据外部服务对用户/pwd进行身份验证

目前,我有一个虚拟验证器,它应该为每个用户名/pwd对用户进行身份验证,并用默认声明进行响应

在身份验证调用过程中,我遇到了一个KeyClope异常,不知道如何修复它。。看来keydove在会话中仍然看不到authenticate user

keycloak_1  | 13:32:10,638 INFO  [stdout] (default task-1) query params - io.undertow.servlet.util.IteratorEnumeration@278071bb
keycloak_1  | 13:32:10,639 INFO  [stdout] (default task-1) formData - {password=[test], grant_type=[password], username=[test]}
keycloak_1  | 13:32:10,644 INFO  [stdout] (default task-1) URI path params - {protocol=[openid-connect], realm=[retail-kasa]}
keycloak_1  | 13:32:10,645 INFO  [stdout] (default task-1) Authentication - UserName + test
keycloak_1  | 13:32:10,647 INFO  [stdout] (default task-1) Authentication - Pwd + test
keycloak_1  | 13:32:10,656 INFO  [stdout] (default task-1) User session - org.keycloak.models.sessions.infinispan.AuthenticationSessionAdapter@437f0e65
keycloak_1  | 13:32:10,660 INFO  [stdout] (default task-1) User in session - null
keycloak_1  | 13:32:10,660 INFO  [stdout] (default task-1) Authentication OK
keycloak_1  | 13:32:10,672 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (default task-1) Uncaught server error: org.keycloak.authentication.AuthenticationFlowException
keycloak_1  |   at org.keycloak.keycloak-services@11.0.2//org.keycloak.authentication.AuthenticationProcessor.authenticateOnly(AuthenticationProcessor.java:981)
这是我的虚拟验证器代码

    package cz.ifortuna.keycloak;

import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.authenticators.directgrant.AbstractDirectGrantAuthenticator;
import org.keycloak.events.Errors;
import org.keycloak.models.*;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.CredentialRepresentation;

import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;

public class KomDirectGrantAuthenticator extends AbstractDirectGrantAuthenticator {

    public static final String PROVIDER_ID = "kom-direct-grant-validate-password";


    @Override
    public void authenticate(AuthenticationFlowContext context) {


        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
        System.out.println("query params - " + context.getHttpRequest().getAttributeNames());
        System.out.println("formData - " + formData);
        System.out.println("URI path params - " + context.getUriInfo().getPathParameters());

        final String userName = formData.getFirst("username");
        final String pwd = formData.getFirst("password");

        System.out.println("Authentication - UserName + " + userName);
        System.out.println("Authentication - Pwd + " + pwd);

        // Creating dummy user model from the scratch
        KomUserModel userModel = new KomUserModel(context.getSession(), context.getRealm(), new KomComponentModel(), 0L, userName);
        context.setUser(userModel);

        // TODO - call external service (now always valid)
        boolean valid = true;
        if (!valid) {
            context.getEvent().user(context.getUser());
            context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
            Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
            context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
            return;
        }

        System.out.println("User session - " + context.getAuthenticationSession());
        //context.attachUserSession(new KomSessionModel());
        System.out.println("User in session - " + context.getAuthenticationSession().getAuthenticatedUser());

        // User is authenticated
        context.getAuthenticationSession().setAuthenticatedUser(userModel);
        context.success();
        System.out.println("Authentication OK");
    }

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

    @Override
    public boolean configuredFor(KeycloakSession keycloakSession, RealmModel realmModel, UserModel userModel) {
        return true;
    }

    @Override
    public void setRequiredActions(KeycloakSession keycloakSession, RealmModel realmModel, UserModel userModel) {

    }

    @Override
    public String getDisplayType() {
        return "kom-direct-grant";
    }

    @Override
    public String getReferenceCategory() {
        return null;
    }

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

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

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

    @Override
    public String getHelpText() {
        return "Validates the password supplied as a 'password' form parameter in direct grant request against KOM";

    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        List<ProviderConfigProperty> configProperties = new ArrayList<>();
        ProviderConfigProperty property = new ProviderConfigProperty();
        property.setName("KOM_URL");
        property.setLabel("Komunikator URL");
        property.setType(ProviderConfigProperty.STRING_TYPE);
        property.setHelpText("KOM URL for authentication requests");
        configProperties.add(property);
        return configProperties;
    }

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

    protected String retrievePassword(AuthenticationFlowContext context) {
        MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
        return inputData.getFirst(CredentialRepresentation.PASSWORD);
    }
}

包cz.ifortuna.keydove;
导入org.keydape.authentication.AuthenticationFlowContext;
导入org.keydape.authentication.AuthenticationFlowError;
导入org.keydape.authentication.authenticators.directgrant.AbstractDirectGrantAuthenticator;
导入org.keydape.events.Errors;
导入org.keydape.models.*;
导入org.keydepot.provider.ProviderConfigProperty;
导入org.keydeport.representations.idm.CredentialRepresentation;
导入javax.ws.rs.core.MultivaluedMap;
导入javax.ws.rs.core.Response;
导入java.util.ArrayList;
导入java.util.List;
公共类KomDirectGrantAuthenticator扩展了AbstractDirectGrantAuthenticator{
公共静态最终字符串提供程序\u ID=“kom直接授予验证密码”;
@凌驾
公共void身份验证(AuthenticationFlowContext上下文){
多值Map formData=context.getHttpRequest().getDecodedFormParameters();
System.out.println(“查询参数-”+context.getHttpRequest().getAttributeNames());
System.out.println(“formData-”+formData);
System.out.println(“URI路径参数-”+context.getUriInfo().getPathParameters());
最终字符串userName=formData.getFirst(“用户名”);
最终字符串pwd=formData.getFirst(“密码”);
System.out.println(“身份验证-用户名+”+用户名);
System.out.println(“身份验证-Pwd+”+Pwd);
//从头开始创建虚拟用户模型
KomUserModel userModel=新的KomUserModel(context.getSession(),context.getRealm(),新的KomComponentModel(),0L,用户名);
setUser(userModel);
//TODO-调用外部服务(现在始终有效)
布尔有效=真;
如果(!有效){
context.getEvent().user(context.getUser());
context.getEvent().error(错误。用户凭据无效);
Response challengeResponse=errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(),“无效的授权”,“无效的用户凭据”);
context.failure(AuthenticationFlowError.INVALID_USER,challengeResponse);
返回;
}
System.out.println(“用户会话-”+context.getAuthenticationSession());
//attachUserSession(新KomSessionModel());
System.out.println(“会话中的用户-”+context.getAuthenticationSession().getAuthenticatedUser());
//用户已通过身份验证
context.getAuthenticationSession().setAuthenticatedUser(userModel);
context.success();
System.out.println(“验证正常”);
}
@凌驾
公共布尔请求器(){
返回false;
}
@凌驾
为(keydeposition keydeposition、realmodel realmodel、UserModel UserModel)配置的公共布尔值{
返回true;
}
@凌驾
public void setRequiredActions(keydoveSession keydoveSession、realmodel realmodel、UserModel UserModel){
}
@凌驾
公共字符串getDisplayType(){
返回“kom直接赠款”;
}
@凌驾
公共字符串getReferenceCategory(){
返回null;
}
@凌驾
公共布尔值可配置(){
返回true;
}
@凌驾
公共身份验证ExecutionModel.Requirement[]getRequirementChoices(){
退货要求和选择;
}
@凌驾
公共布尔值isUserSetupAllowed(){
返回false;
}
@凌驾
公共字符串getHelpText(){
return“验证针对KOM的直接授权请求中作为‘password’表单参数提供的密码”;
}
@凌驾
公共列表getConfigProperties(){
List configProperties=new ArrayList();
ProviderConfigProperty属性=新ProviderConfigProperty();
设置名称(“KOM_URL”);
setLabel(“Komunikator URL”);
setType(ProviderConfigProperty.STRING\u类型);
property.setHelpText(“用于身份验证请求的KOM URL”);
添加(属性);
返回配置属性;
}
@凌驾
公共字符串getId(){
返回提供者ID;
}
受保护的字符串检索密码(AuthenticationFlowContext上下文){
多值Map inputData=context.getHttpRequest().getDecodedFormParameters();
返回inputData.getFirst(CredentialRepresentation.PASSWORD);
}
}
你能给我指出解决办法吗?我是否需要为用户存储库实现自定义SPI?在我的用例中,我只想将用户pwd身份验证委托给外部服务

谢谢