使用 CustomFormAuth 和 o:form 登录未正确转发

Login not forwarding correctly with CustomFormAuth and o:form

我正在尝试在我的 Web 项目上实施 Jakarta Security,为此我遵循了 Bauke Scholtz 和 Arjan Tijms 的惊人的“Java EE 8 中的 JSF 权威指南”,但它好像撞墙了

当我在地址栏中输入受限制的 url 时,转发正常进行,我看到了登录页面,但在提交表单后,页面没有重定向,登录页面似乎被重新加载(字段被清空,但地址栏中的url仍然是受限页面中的那个。

在日志的帮助下我知道 AuthenticationStatus returns 和 SUCCESS 但即便如此,如果我手动将地址栏更改为另一个受限制的 url 它被转发到登录页面。

如果我在地址栏中输入 /login 并提交身份验证,重定向就会正常进行。

似乎如果我让 useForwardToLogin = false 它工作正常但是然后 getForwardURL 总是 returns null 并且我无法将用户重定向到他们想要访问的页面。

感谢您的帮助。

ApplicationConfig.java

@CustomFormAuthenticationMechanismDefinition(
        loginToContinue = @LoginToContinue(
                loginPage = "/login",
                errorPage = ""
        )
)
@FacesConfig
@ApplicationScoped
public class ApplicationConfig {

}

login.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:c="http://java.sun.com/jsp/jstl/core"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
                xmlns:p="http://primefaces.org/ui"
                xmlns:o="http://omnifaces.org/ui"
                template="/WEB-INF/templates/login.xhtml">
    <ui:define name="content">
        <c:choose>
            <c:when test="#{not empty request.userPrincipal}">
                <div>Already logged</div>
            </c:when>
            <c:otherwise>
                <o:form>
                    <p:inputText id="login" value="#{loginBacking.userName}" required="true"/>
                    <p:password id="pwd" value="#{loginBacking.password}" required="true"/>
                    <p:inputNumber id="otp" value="#{loginBacking.otp}" />
                    
                    <o:messages id="messages" var="message">
                        <div class="#{fn:toLowerCase(message.severity)}">#{message.summary}</div>
                    </o:messages>

                    <p:commandButton value="Submit" action="#{loginBacking.login}" update="@form"/>
                </o:form>
            </c:otherwise>
        </c:choose>
    </ui:define>
</ui:composition>

LoginBacking.java

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

    @Getter @Setter private String userName;
    @Getter @Setter private String password;
    @Getter @Setter private Integer otp;

    public void login() {
        switch (continueAuthentication()) {
            case SEND_CONTINUE:
                Faces.responseComplete();
                break;
            case SEND_FAILURE:
                Messages.addGlobalFatal("Login failed.");
                break;
            case SUCCESS:
                redirect();
                break;
        }
    }

    private AuthenticationStatus continueAuthentication() {
        return this.securityContext.authenticate(
                Faces.getRequest(),
                Faces.getResponse(),
                AuthenticationParameters.withParams()
                        .newAuthentication(getForwardURL() == null)
                        .credential(new CustomCredential(userName, password, otp))
        );
    }

    private void redirect() {
        CustomUser user = securityContext.getPrincipalsByType(CustomPrincipal.class).stream()
                .map(CustomPrincipal::getCustomUser)
                .findAny()
                .orElse(null);

        if (user == null) Messages.addGlobalFatal("Something went wrong.");

        String forwardURL = getForwardURL();
        if (forwardURL != null) {
            Faces.redirect(forwardURL);
            return;
        }
        
        Faces.redirect(Faces.getRequestContextPath() + "/restricted/index");
    }

    public void logout() throws ServletException {
        Faces.logout();
        Faces.invalidateSession();
        Faces.redirect("login");
    }

    public String getForwardURL() {
        String requestURI = Faces.getRequestAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
        String queryString = Faces.getRequestAttribute(RequestDispatcher.FORWARD_QUERY_STRING);

        if (requestURI == null) return null;
        return (queryString == null) ? requestURI : (requestURI + "?" + queryString);
    }
}

仅供参考,如您所见,我正在使用 Omnifaces、Primefaces 和 Lombok。

根据 Kukeltje 的建议,我制作了一个最小的工作示例,您可以在此处找到 https://github.com/Pilpin/mwe-jakartasecurity。 它是 Bauke Scholtz 和 Arjan Tijms 所著的“Java EE 8 中 JSF 的权威指南”一书的 repo 安全部分的修改分支 => https://github.com/Apress/definitive-guide-to-jsf-javaee8.

它工作得很好,所以我决定向它添加 omnifaces 和 primefaces 并意识到:

  1. 如此处所说 如果您使用的是 primefaces' p:commandButton,则应向其添加 ajax=false 属性。
  2. 如果您使用的是 omnifaces' o:form,您应该添加 useRequestURI="false" 属性。这是有道理的,因为我的 fork 使用 forward 来访问登录页面,所以 URL 和 URI 是你想要访问的任何页面,当 useRequestURI 设置为 true (默认)在 o:form 上它提交表单 to the exact request URI 所以不到登录页面。

非常感谢 Kukeltje 将我推向正确的方向。