在同一页面上显示错误消息

时间:2017-05-24 15:10:32

标签: jsf

我有一个登录表单,用于使用LDAP对活动目录对用户进行身份验证。我使用Apache Shiro来做这件事(我相信这不是重要的事情,但我最好提一下,因为它可能对我试图开始工作产生影响)。如果用户输入了错误的密码,则会抛出异常,我想在我的表单所在的同一页面上显示错误消息。因此,我在建议的地方写了一个自定义异常处理程序,指定错误消息。以下是代码的相关部分:

index.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:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
>

<h:form>
    <h:messages globalOnly="false"/>
    <h:panelGrid columns="2">
        <h:outputLabel for="username">Username:</h:outputLabel>
        <h:inputText id="username" value="#{user.userName}"/>
        <h:outputLabel for="password">Password:</h:outputLabel>
        <h:inputSecret id="password" value="#{user.password}"/>
        <h:message for="username" infoStyle="color:darkgreen" errorStyle="color:red"/>
        <h:message for="password" infoStyle="color:darkgreen" errorStyle="color:red"/>
    </h:panelGrid>
    <h:commandButton value="Login" action="#{loginController.login}" rendered="#{!loginController.loggedIn}"/>
    <h:commandButton value="Logout" action="#{loginController.logout}" rendered="#{loginController.loggedIn}"/>
</h:form>
</html>

LoginController类的登录方法:

public String login() {
        currentUser = ldapUserProvider.provide(); //gets Apache Shiro's Subject which is the user being authenticated.
        if (!currentUser.isAuthenticated()) {
            CustomUsernamePasswordToken token = new CustomUsernamePasswordToken(DOMAIN, user.getUserName(), user.getPassword());
            token.setRememberMe(true);
            try {
                currentUser.login(token); //this one causes exception if the password is wrong, for example
                loggedIn = true;
                return "success.xhtml";
            } catch (Exception e) {
                return "index.xhtml"; //in this case I'd like to show the error message and prompt to login again
            }
        }
        return "index.xhtml";
    }

自定义ExceptionHandler:

package com.flyeralarm.tools.apss;

import javax.faces.application.FacesMessage;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.context.Flash;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import java.util.Iterator;

public class AuthenticationExceptionHanlder extends ExceptionHandlerWrapper {
    private ExceptionHandler wrappedExceptionhandler;

    public AuthenticationExceptionHanlder(final ExceptionHandler wrappedExceptionhandler) {
        this.wrappedExceptionhandler = wrappedExceptionhandler;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return wrappedExceptionhandler;
    }

    @Override
    public void handle() {
        Iterator<ExceptionQueuedEvent> eventIterator = getUnhandledExceptionQueuedEvents().iterator();
        while (eventIterator.hasNext()) {
            ExceptionQueuedEvent event = eventIterator.next();
            ExceptionQueuedEventContext eventContext = (ExceptionQueuedEventContext) event.getSource();
            Throwable exception = eventContext.getException();
            handleException(exception, eventContext);
            eventIterator.remove();
            getWrapped().handle();
        }
    }

    private void handleException(Throwable exception, ExceptionQueuedEventContext context) {
        FacesMessage exceptionMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Login failed.", exception.getMessage());
        FacesContext facesContext = FacesContext.getCurrentInstance();
        Flash flash = facesContext.getExternalContext().getFlash(); //I read that putting the message into flash would do the work but it didn't
        flash.put("errorDetails", exception.getMessage());
        facesContext.addMessage(null, exceptionMessage);
        facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, "index?faces-redirect=true");
    }
}

我还尝试了仅使用global = true和false的不同组合,但页面仍然没有显示我在自定义异常处理程序中定义的错误消息。我在做什么?错?

重复的问题免责声明:我还在SO网站上搜索了可能的解决方案,并尝试了不同答案中提出的建议,但没有人提供帮助。

2 个答案:

答案 0 :(得分:1)

如果在支持bean中正确添加消息,globalOnly开关应该可以正常工作。 FacesContext.addMessage(...)中的client-id必须为null:

FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(message));

答案 1 :(得分:0)

尝试将捕获中的回报从return "index.xhtml";更改为return null;,这可能会导致您的网页完全重新加载,从而导致错误状态丢失,或者您可以尝试在{{1方法

AuthenticationExceptionHanlder#handleException

应该向jsf指出存在验证错误,以防止刷新发生。