我正在尝试构建自定义DirectGrantAuthenticator,将来应该进行网络调用以针对外部服务对用户/密码进行身份验证。
目前,我有一个虚拟的身份验证器,应该为每个用户名/密码验证用户身份并使用默认声明进行响应。
我在身份验证呼叫期间正在与Keycloak异常作斗争,并且不知道如何解决它。.看来Keycloak在会话中仍然看不到身份验证用户
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);
}
}
您能指出我的解决方案吗?我需要为用户存储库实现自定义SPI吗?在我的用例中,我只想将用户pwd身份验证委派给外部服务...
谢谢