为什么来自 Soteria 的这个简单的 Jakarta Security 示例可以在 Payara 上运行,但不能在 WildFly 上运行?

Why does this simple Jakarta Security example from Soteria work on Payara but not on WildFly?

我习惯使用 WildFly 进行 Java/Jakarta EE 开发,最近我想将一个使用 JAAS 进行 authentication/authorization 的项目更新到 Jakarta 的新 Jakarta Security API EE 9.1.

我无法让它工作,所以我决定尽可能创建最简单的示例并在不同的应用程序服务器上进行试验。我从 Soteria (as I understand, the reference implementation for Jakarta Security) and created a new Jakarta EE project to deploy on my application servers. The code is available here.

中举了一个例子

该示例包含一个非常简单的身份存储,用于用户 reza 和密码 secret1:

@ApplicationScoped
public class TestIdentityStore implements IdentityStore {
    public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) {

        if (usernamePasswordCredential.compareTo("reza", "secret1")) {
            return new CredentialValidationResult("reza", new HashSet<>(asList("foo", "bar")));
        }

        return INVALID_RESULT;
    }
}

一个 servlet 包含自定义表单身份验证机制定义,如果用户未通过身份验证,该定义会将用户重定向到 /login.jsf。通过身份验证后,它会显示用户名(主体名称)并测试用户角色。还有一个注销功能:

@CustomFormAuthenticationMechanismDefinition(
    loginToContinue = @LoginToContinue(
        loginPage="/login.jsf",
        errorPage="" // DRAFT API - must be set to empty for now
    )
)

@WebServlet("/servlet")
@DeclareRoles({ "foo", "bar", "kaz" })
@ServletSecurity(@HttpConstraint(rolesAllowed = "foo"))
public class Servlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String webName = null;
        if (request.getUserPrincipal() != null) {
            webName = request.getUserPrincipal().getName();
        }
        
        response.getWriter().write(
                "<html><body> This is a servlet <br><br>\n" +
        
                    "web username: " + webName + "<br><br>\n" +
                            
                    "web user has role \"foo\": " + request.isUserInRole("foo") + "<br>\n" +
                    "web user has role \"bar\": " + request.isUserInRole("bar") + "<br>\n" +
                    "web user has role \"kaz\": " + request.isUserInRole("kaz") + "<br><br>\n" + 

                        
                    "<form method=\"POST\">" +
                        "<input type=\"hidden\" name=\"logout\" value=\"true\"  >" +
                        "<input type=\"submit\" value=\"Logout\">" +
                    "</form>" +
                "</body></html>");
    }
    
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if ("true".equals(request.getParameter("logout"))) {
            request.logout();
            request.getSession().invalidate();
        }
        
        doGet(request, response);
    }
}

如果用户重定向到 /login.jsf:

,则呈现 login.xhtml 页面
<!DOCTYPE html>
<html lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:jsf="http://xmlns.jcp.org/jsf">

    <h:head/>
    
    <h:messages />
    
    <body>
        <p>
            Login to continue
        </p>
    
         <form jsf:id="form">
            <p>
                <strong>Username </strong> 
                <input jsf:id="username" type="text" jsf:value="#{loginBacking.username}" />
            </p>
            <p>
                <strong>Password </strong> 
                <input jsf:id="password" type="password" jsf:value="#{loginBacking.password}" />
            </p>
            <p>
                <input type="submit" value="Login" jsf:action="#{loginBacking.login}" />
            </p>
        </form>
    </body>
</html>

最后,JSF backing bean 负责处理表单并调用 Jakarta Securty API 来执行身份验证:

@Named
@RequestScoped
public class LoginBacking {
    @Inject
    private SecurityContext securityContext;

    @NotNull
    @Size(min = 3, max = 15, message="Username must be between 3 and 15 characters")
    private String username;
    
    @NotNull
    @Size(min = 5, max = 50, message="Password must be between 5 and 50 characters")
    private String password;
    
    public void login() {
        FacesContext context = FacesContext.getCurrentInstance();
        Credential credential = new UsernamePasswordCredential(username, new Password(password));
        
        AuthenticationStatus status = securityContext.authenticate(
            getRequest(context),
            getResponse(context), 
            withParams().credential(credential));
        
        if (status.equals(SEND_CONTINUE)) {
            // Authentication mechanism has send a redirect, should not
            // send anything to response from JSF now.
            context.responseComplete();
        } else if (status.equals(SEND_FAILURE)) {
            addError(context, "Authentication failed");
        }
        
    }
    
    private static HttpServletResponse getResponse(FacesContext context) {
        return (HttpServletResponse) context.getExternalContext().getResponse();
    }
    
    private static HttpServletRequest getRequest(FacesContext context) {
        return (HttpServletRequest) context.getExternalContext().getRequest();
    }
    
    private static void addError(FacesContext context, String message) {
        context.addMessage(null, new FacesMessage(SEVERITY_ERROR, message, null));
    }

    /* Getters and setters. */
}

正如我所说,它在 Payara 上按预期工作,但在 WildFly 上出现以下异常:

ERROR [io.undertow.request] (default task-1) UT005023: Exception handling request to /jakarta-security-example/login.jsf: jakarta.servlet.ServletException: java.lang.IllegalStateException: java.io.IOException: java.io.IOException: ELY01177: Authorization failed.
    at jakarta.faces.api//jakarta.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:699)
    at jakarta.faces.api//jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:437)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
    at io.opentracing.contrib.opentracing-jaxrs2//io.opentracing.contrib.jaxrs2.server.SpanFinishingFilter.doFilter(SpanFinishingFilter.java:52)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletChain.handleRequest(ServletChain.java:68)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at org.wildfly.security.elytron-web.undertow-server@1.10.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.lambda$handleRequest(ElytronRunAsHandler.java:68)
    at org.wildfly.security.elytron-base@1.17.1.Final//org.wildfly.security.auth.server.FlexibleIdentityAssociation.runAsFunctionEx(FlexibleIdentityAssociation.java:103)
    at org.wildfly.security.elytron-base@1.17.1.Final//org.wildfly.security.auth.server.Scoped.runAsFunctionEx(Scoped.java:161)
    at org.wildfly.security.elytron-base@1.17.1.Final//org.wildfly.security.auth.server.Scoped.runAs(Scoped.java:73)
    at org.wildfly.security.elytron-web.undertow-server@1.10.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.handleRequest(ElytronRunAsHandler.java:67)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.core@2.2.12.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.core@2.2.12.Final//io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:53)
    at io.undertow.core@2.2.12.Final//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:59)
    at io.undertow.core@2.2.12.Final//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.0.Final//org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38)
    at io.undertow.core@2.2.12.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
    at io.undertow.core@2.2.12.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
    at io.undertow.core@2.2.12.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:280)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.access0(ServletInitialHandler.java:79)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.call(ServletInitialHandler.java:134)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.call(ServletInitialHandler.java:131)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction.call(ContextClassLoaderSetupAction.java:43)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create[=14=](UndertowDeploymentInfoService.java:1544)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create[=14=](UndertowDeploymentInfoService.java:1544)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create[=14=](UndertowDeploymentInfoService.java:1544)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create[=14=](UndertowDeploymentInfoService.java:1544)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:260)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.access[=14=]0(ServletInitialHandler.java:79)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleRequest(ServletInitialHandler.java:100)
    at io.undertow.core@2.2.12.Final//io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)
    at io.undertow.core@2.2.12.Final//io.undertow.server.HttpServerExchange.run(HttpServerExchange.java:852)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
    at org.jboss.xnio@3.8.4.Final//org.xnio.XnioWorker$WorkerThreadFactory.run(XnioWorker.java:1280)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: jakarta.faces.el.EvaluationException: java.lang.IllegalStateException: java.io.IOException: java.io.IOException: ELY01177: Authorization failed.
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.application.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:77)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.application.ActionListenerImpl.getNavigationOutcome(ActionListenerImpl.java:75)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:64)
    at jakarta.faces.api//jakarta.faces.component.UICommand.broadcast(UICommand.java:213)
    at jakarta.faces.api//jakarta.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:852)
    at jakarta.faces.api//jakarta.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1329)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:56)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.lifecycle.Phase.doPhase(Phase.java:72)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:159)
    at jakarta.faces.api//jakarta.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:681)
    ... 52 more
Caused by: java.lang.IllegalStateException: java.io.IOException: java.io.IOException: ELY01177: Authorization failed.
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.Jaspic.handleCallbacks(Jaspic.java:184)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.Jaspic.notifyContainerAboutLogin(Jaspic.java:157)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.HttpMessageContextImpl.notifyContainerAboutLogin(HttpMessageContextImpl.java:261)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.HttpMessageContextImpl.notifyContainerAboutLogin(HttpMessageContextImpl.java:239)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.CustomFormAuthenticationMechanism.validateRequest(CustomFormAuthenticationMechanism.java:53)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.CustomFormAuthenticationMechanism$Proxy$_$$_WeldSubclass.validateRequest$$super(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.TerminalAroundInvokeInvocationContext.proceedInternal(TerminalAroundInvokeInvocationContext.java:51)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:78)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.cdi.LoginToContinueInterceptor.processContainerInitiatedAuthentication(LoginToContinueInterceptor.java:182)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.cdi.LoginToContinueInterceptor.validateRequest(LoginToContinueInterceptor.java:98)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.cdi.LoginToContinueInterceptor.intercept(LoginToContinueInterceptor.java:76)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.NonTerminalAroundInvokeInvocationContext.proceedInternal(NonTerminalAroundInvokeInvocationContext.java:66)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:78)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.cdi.AutoApplySessionInterceptor.intercept(AutoApplySessionInterceptor.java:65)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeAroundInvoke(InterceptorMethodHandler.java:84)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeInterception(InterceptorMethodHandler.java:72)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.invoke(InterceptorMethodHandler.java:56)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:79)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:68)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.CustomFormAuthenticationMechanism$Proxy$_$$_WeldSubclass.validateRequest(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.bean.proxy.AbstractBeanInstance.invoke(AbstractBeanInstance.java:38)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:106)
    at deployment.jakarta-security-example.war//jakarta.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism47370719$Proxy$_$$_WeldClientProxy.validateRequest(Unknown Source)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.HttpBridgeServerAuthModule.validateRequest(HttpBridgeServerAuthModule.java:90)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.DefaultServerAuthContext.validateRequest(DefaultServerAuthContext.java:53)
    at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.0.Final//org.wildfly.elytron.web.undertow.server.servlet.ServletSecurityContextImpl.authenticate(ServletSecurityContextImpl.java:182)
    at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.0.Final//org.wildfly.elytron.web.undertow.server.servlet.ServletSecurityContextImpl.authenticate(ServletSecurityContextImpl.java:99)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.spec.HttpServletRequestImpl.authenticate(HttpServletRequestImpl.java:475)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.Jaspic.authenticate(Jaspic.java:91)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.SecurityContextImpl.authenticate(SecurityContextImpl.java:82)
    at deployment.jakarta-security-example.war//org.glassfish.soteria.test.LoginBacking.login(LoginBacking.java:59)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.glassfish.jakarta.el@4.0.0//com.sun.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:152)
    at org.glassfish.jakarta.el@4.0.0//com.sun.el.parser.AstValue.invoke(AstValue.java:261)
    at org.glassfish.jakarta.el@4.0.0//com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:237)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.module.web.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.module.web.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.module.web.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.module.web.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:70)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.application.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:67)
    ... 61 more
Caused by: java.io.IOException: java.io.IOException: ELY01177: Authorization failed.
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext.handle(JaspiAuthenticationContext.java:112)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.Jaspic.handleCallbacks(Jaspic.java:178)
    ... 122 more
Caused by: java.io.IOException: ELY01177: Authorization failed.
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext.handleOne(JaspiAuthenticationContext.java:189)
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext.lambda$handle[=14=](JaspiAuthenticationContext.java:101)
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.SecurityActions.doPrivileged(SecurityActions.java:39)
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext.handle(JaspiAuthenticationContext.java:100)
    ... 123 more

请注意,我的项目不包含 Soteria 示例中的 jboss-web.xml 文件,但如果我添加它,则在使用登录页面时每次验证都会失败(即使我输入了正确的用户名和密码) .如果我直接打开 http://localhost:8080/jakarta-security-example/servlet 我会得到:

This is a servlet

web username: null

web user has role "foo": false
web user has role "bar": false
web user has role "kaz": false

如果我点击注销按钮,我会得到:jakarta.servlet.ServletException: UT010062: No SecurityContext available

试图在 Google 上找到答案,但找不到,所以我希望 Stack Overflow 中的某个人可能知道问题所在并帮助我。谢谢!

解决方案

WildFly 服务器需要额外配置:

  1. 编辑 other 应用程序安全域, Integrated JASPI 属性 设置为关闭。

  2. 重新加载服务器

还有一个脚本用于: https://github.com/wildfly/quickstart/tree/main/ee-security#configure-the-server

为什么?什么是集成 JASPI?

来自https://docs.wildfly.org/25/WildFly_Elytron_Security.html#Elytron_and_Java_EE_Security

The EE Security API is built on JASPI. Within JASPI we support two different modes of operation 'integrated', and 'non-integrated'. In integrated mode any identity being established during authentication is expected to exist in the associated security domain. With the EE Security APIs however it is quite likely an alternative store will be in use so configuration the mapping to use 'non-integrated' JASPI allows for identities to be dynamically created as required.