如何使用 Jakarta Faces (JSF) 和 Glassfish 处理身份验证失败

问题描述 投票:0回答:1

我正在尝试基于 Jakarta 10.0 Faces 和 Glassfish 7 实现基于简单表单的身份验证。

一个(非常)最小(但有效)的代码显示了该过程:loginout.xhtml 登录调用 LoginOutBean login() 方法。 login() 方法依次调用 Security#authenticate,后者调用 CustomAuthentication 类(实现 HttpAuthenticationMechanism)的 validateRequest() 方法。 现实世界中的 CustomAuthentication 将进行数据库或 LDAP 调用来验证登录请求,返回成功或失败。

但是我无法让失败部分工作。失败不会返回到调用者方法让我显示错误消息。 然而,验证成功确实返回并允许我显示一条消息。 我见过使用 return

httpMsgContext.responseUnauthorized()
的例子,但它对我不起作用,它只是生成一个 401 页面。 我可以在 web.xml 中捕获 401,但这不允许我在登录页面上显示消息。 如下面的代码所示,我尝试了多种方法来返回 SEND_FAILURE 情况(显示失败消息),但没有成功(如代码注释中所述)。

我怀疑我遗漏了一些东西,但我找不到任何有效的示例来处理验证失败。 如果有任何指点,我将不胜感激。

登录.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:em="jakarta.faces.composite/emcomp">

    <h:head>
        <title>Login and logout</title>
    </h:head>

    <h:body>
        <div>Login/logout</div>
        <div>

            <h:form id="loginoutform">
                <h:commandButton id="loginSubmit" type="submit" value="Login" action="#{loginOutBean.login()}"/>

                <br />
                <h:commandButton id="logoutSubmit" type="submit" value="Logout" action="#{loginOutBean.logout}"/>

                <br />
                <h:messages id="globalMsgs" />
            </h:form>

        </div>
    </h:body>
</html>

LoginOutBean 类:

@Named(value = "loginOutBean")
@SessionScoped
public class LoginOutBean implements Serializable {
    private static final long serialVersionUID = 290524L;
    private static final Logger logger = Logger.getLogger("LoginOutBean");

    @Inject private SecurityContext securityContext;
    @Inject private FacesContext facesContext;
    @Inject private ExternalContext externalContext;

    public String login() throws IOException {
        switch( continueAuthentication() ) {
            case SEND_CONTINUE -> {
                logger.log(Level.INFO,"SEND_CONTINUE");
                facesContext.responseComplete();
            }

            case SEND_FAILURE -> {
                logger.log(Level.SEVERE,"SEND_FAILURE");
                // If gets here, following fails with FacesException of 
                // "org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance()"
                // is null
                facesContext.addMessage(null, new FacesMessage(
                        FacesMessage.SEVERITY_ERROR, "Login failed", null));
            }

            case SUCCESS -> {
                logger.info("SUCCESS");
                facesContext.addMessage(null, new FacesMessage(
                        FacesMessage.SEVERITY_INFO, "Login success!", null));
            }

            case NOT_DONE -> {}
        }
        
        // Shouldn't get here
        return null;
    }
    
    public String logout() throws ServletException {
        HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
        
        request.logout();
        request.getSession().invalidate();
        return "/loginout?faces-redirect=true";
    }

    private AuthenticationStatus continueAuthentication() {
        AuthenticationStatus as = securityContext.authenticate(
            (HttpServletRequest) externalContext.getRequest(),
            (HttpServletResponse) externalContext.getResponse(),
            AuthenticationParameters.withParams()
                    .credential(new UsernamePasswordCredential("fred", "mysecret"))
        );
        
        logger.info("Authentication status: " +as.toString());
        return as;
    }
}

自定义身份验证类:

@ApplicationScoped
public class CustomAuthentication implements HttpAuthenticationMechanism {
    private static final Logger logger = Logger.getLogger("CustomAuthentication");
    
    @Override
    public AuthenticationStatus validateRequest( HttpServletRequest request,
                                                 HttpServletResponse response, 
                                                 HttpMessageContext httpMsgContext ) throws AuthenticationException {
        logger.info("****************>>>> validateRequest()...");

        /*
         * "Authentication" fails...
         */
        // On launch, doesn't display login screen but validateRequest() is called
        // which doesn't return to caller, just generates HTTP 404 - Not Found page
//        return httpMsgContext.responseNotFound();

        // On launch, doesn't display login screen but validateRequest() is called
        // which doesn't return to caller, just generates HTTP 401 - Unauthorized
        // page. This is the line seen mostly in examples (eg: Baeldung)
        return httpMsgContext.responseUnauthorized();

        // On launch, doesn't display login screen but validateRequest() is called
        // which doesn't return to caller, just generates blank screen
//        return httpMsgContext.notifyContainerAboutLogin(CredentialValidationResult.NOT_VALIDATED_RESULT);

        // On launch, doesn't display login screen but validateRequest() is called
        // which doesn't return to caller, just generates blank screen
//        return httpMsgContext.notifyContainerAboutLogin(CredentialValidationResult.INVALID_RESULT);

        // On launch, displays login page and validateRequest() is called.
        // Clicking login button then fires authentication *twice* and a whole
        // raft of IllegalStateExceptions are thrown followed by authentication
        // status of SEND_FAILURE or NOT_DONE, returning to caller (which
        // subsequently fails with FacesException).
//        return httpMsgContext.forward("/loginout.xhtml");

        // On launch, doesn't display login page but validateRequest() is called,
        // return is not made to caller, no exceptions are thrown and a blank
        // page is displayed.
//        return AuthenticationStatus.SEND_FAILURE;

        // On launch, doesn't display login page but validateRequest() is called,
        // return is not made to caller, no exceptions are thrown and a blank
        // page is displayed. 
//        return AuthenticationStatus.SEND_CONTINUE;
        
        // On launch, doesn't display login page but validateRequest() is called
        // which doesn't return to caller, instead throws "Jakarta Authentication:
        // Exception during validateRequest" and shows HTTP 500 page
//        return null;


        /*
         * "Authentication" success...
         */
        // Following works...
        // On launch, displays login page, clicking login button returns Success
        // to caller which displays success message.
//        return httpMsgContext.doNothing();

        // Following works...
        // On launch, displays login page, clicking login button returns Success
        // to caller which displays success message.
//        return AuthenticationStatus.SUCCESS;

        // Following works...
        // On launch, displays login page, clicking login button returns Success
        // to caller which displays success message.
//        return httpMsgContext.notifyContainerAboutLogin("fred", Set.of("user"));
    }
}
jsf jakarta-ee glassfish jakarta-ee-security-api
1个回答
© www.soinside.com 2019 - 2024. All rights reserved.