jsf spring安全性登录错误消息

问题描述 投票:3回答:2

我在我的jsf2 Web应用程序中使用Spring Security 3。

我如何在我的登录表单中显示错误的凭据消息,而又不将get param?login_error附加到authenticated-fail-login-page?

我曾尝试使用本教程所述的阶段监听器:

http://tutorials.slackspace.de/tutorial/Custom-login-page-with-JSF-and-Spring-Security-3

但是它不起作用。

都不使用preRenderView侦听器。

并且都没有检查Spring Security的最后一个异常以呈现消息。

有什么想法吗?

UPDATE:

我的登录页面:

<f:metadata>
<f:viewParam name="error" value="#{autenticacionController.error}" />
<f:event listener="#{autenticacionController.comprobarAuthException}" type="preRenderView" />
</f:metadata>
<h:messages globalOnly="true" layout="table" />
<h:form id="formLogin" prependId="false">
<h:outputLabel for="j_username" value="Usuario:" />
<h:inputText id="j_username" value="#{autenticacionController.administrador.login}" />
<h:outputLabel for="j_password" value="Contraseña:" />
<h:inputSecret id="j_password" value="#{autenticacionController.administrador.password}" />
<h:commandButton value="Entrar" action="#{autenticacionController.loginAction}" />
<h:commandButton value="Cancelar" immediate="true" action="#{autenticacionController.cancelarAction}" />
</h:form>

我的托管bean:

@ManagedBean(name="autenticacionController")
@RequestScoped
public class AutenticacionController extends BaseController {

    //entidad "administrador" contra el que validar los campos del form login
    private Administrador administrador = new Administrador();

    //propiedad de spring-security (true si el usuario no es anónimo)
    @SuppressWarnings("unused")
    private boolean autenticado;

    //propiedad para guardar el param GET si hubo fallo en la autenticación de SS
    private int error;

    //Constructor vacío del Backing Bean controlador
    public AutenticacionController() {
        log.info("Creación del backing bean AutenticacionController");
    }

    @PostConstruct
    public void init() {
        //inicializar atributos del backing bean
        log.info("PostConstruct del backing bean BarcoController");
    }


    //Getters y setters de atributos del backing bean
    public Administrador getAdministrador() {
        return administrador;
    }
    public void setAdministrador(Administrador administrador) {
        this.administrador = administrador;
    }

    public boolean isAutenticado() {
        Authentication autenticacion = SecurityContextHolder.getContext().getAuthentication();
        boolean resultado = (autenticacion != null) &&
                            !(autenticacion instanceof AnonymousAuthenticationToken) &&
                            autenticacion.isAuthenticated();
        return resultado;
    }

    public int getError() {
        return error;
    }
    public void setError(int error) {
        this.error = error;
    }

    //MÉTODO LISTENER del evento preRenderView en la página login.
    //Para comprobar si la autenticación de Spring Security falló (error=1).
    //En ese caso muestra el error con un faces message.
    public void comprobarAuthException (ComponentSystemEvent event){
        log.info("listener comprobarAuth");
        if (error==1) {
            String msj = "";
            Exception e = (Exception) UtilJsf.getParamSessionMap(WebAttributes.AUTHENTICATION_EXCEPTION);
            log.info("SSexception = "+((e==null)?"null":e.getMessage()));
            if (e != null) {
                String ultimoUsuario = (String) UtilJsf.getParamSessionMap(WebAttributes.LAST_USERNAME);
                log.info("SS last_username = "+ultimoUsuario);
                administrador.setLogin(ultimoUsuario);
                if (e instanceof BadCredentialsException) {
                    msj = UtilJsf.getMsjProperties("msjsInfo", "UsuPwdIncorrectos");
                } else {
                    msj = UtilJsf.getMsjProperties("msjsInfo", "ErrorAutenticacion");
                }
                UtilJsf.mostrarFacesMsjGlobal(msj);
            }
        }
        return;
    }


    /* ******************************* */
    /* Métodos "action" del form login */
    /* ******************************* */

    // EVENTO: Pulsar el botón "entrar" del form login
    // Reenviar(FORWARD) la petición a la URL "/j_spring_security_check" para autenticarse
    // También se debe configurar el filtro de spring-security para que procese forwards
    public void loginAction () {
        try {
            FacesContext.getCurrentInstance().getExternalContext().dispatch("/j_spring_security_check");
        } catch (IOException e) {
        }
    }

    // EVENTO: Pulsar el boton "cancelar" en el form login
    // No hacer nada --> Ir a la pantalla de inicio de la aplic
    public String cancelarAction () {
        return "/inicio";
    }

}

在我的Spring Security配置中,我有:

authentication-failure-url="/faces/paginas/autenticacion/login.xhtml?error=1"

如果我从登录页面中删除错误参数和viewParam,并在侦听器中仅检查Spring Security异常,则它不起作用。

感谢注销,我的处理方法与此类似,为此提供了以下链接:

<h:outputLink value="#{request.contextPath}/j_spring_security_logout" rendered="#{autenticacionController.autenticado}">Cerrar sesión (Administrador)</h:outputLink>
jsf login spring-security message
2个回答
7
投票

我倾向于在preRenderView上使用<f:event>,它将更新表单上的消息组件。这就是我的方法。

<f:event listener="#{loginBean.updateMessages(true)}" type="preRenderView" />
        <div style="margin-left: 50px; width: 500px;"><br />
        <h:form id="loginForm" prependId="false">

            <p:messages id="errorMessages" />
            <label for="j_username">
                <h:outputText value="Username:" /><br />
            </label>
            <h:inputText id="j_username" required="true" width="500" style="width: 300px;" />

            <br />
            <br />
            <label for="j_password">
                <h:outputText value="Password:" /><br />
            </label>
            <h:inputSecret id="j_password" required="true" width="500" style="width: 300px;" />
            &nbsp;<h:link value="Forgot my password" outcome="forgotpassword" /> 
            <br />
            <br />
            <label for="_spring_security_remember_me">
                <h:outputText value="Remember me" />
            </label>
            <h:selectBooleanCheckbox id="_spring_security_remember_me" />
            <br /><br />
            <p:commandButton ajax="false" type="submit" id="login" action="#{loginBean.doLogin}" value="Login" update="errorMessages" />
        </h:form>
        </div>

然后在我的LoginBean托管Bean中,将请求同样转发到Spring Security servlet上,并更新消息。如果您有兴趣了解我也是如何解决该问题的,您会注意到我也有注销操作的代码。

private String username;
private String password;    

public String getUsername() {
    return username;
}

public void setUsername(final String username) {
    this.username = username.trim();
}

public String getPassword() {
    return password;
}

public void setPassword(final String password) {
    this.password = password.trim();
}

public void updateMessages(boolean update) throws Exception {
    System.out.println("Start LoginBean.updateMessages");
    ex = (Exception)FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
        .get(WebAttributes.AUTHENTICATION_EXCEPTION);

if (ex != null) {
    log.error("Authentication Failed! ", ex);
    System.err.println("Authentication Failed! " + ex.getMessage());
    FacesContext.getCurrentInstance().addMessage(null,
            new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), ex.getMessage()));
}
    System.out.println("End LoginBean.updateMessages");
}

public String doLogin() {
    log.info("Start LoginBean.doLogin");
    try {
        ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();

        RequestDispatcher dispatcher = ((ServletRequest) context.getRequest())
                 .getRequestDispatcher("/j_spring_security_check");

        dispatcher.forward((ServletRequest) context.getRequest(),
                (ServletResponse) context.getResponse());

        FacesContext.getCurrentInstance().responseComplete();
        // It's OK to return null here because Faces is just going to exit.
    } catch (Exception e) {
        log.error("Exception doLogin", e);
    } finally {
        log.info("End LoginBean.doLogin");
    }
return "";
}

public String logout() {
    FacesContext context = FacesContext.getCurrentInstance();
    Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
    if (!sessionMap.containsKey("sessionBean"))
        return "";

    SessionBean sessionBean = (SessionBean)sessionMap.get("sessionBean");
    log.info("Logging out user: " + sessionBean.getLoggedInUser().getUsername());

    sessionMap.remove("sessionBean");

    //HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
    //session.invalidate();
RequestDispatcher dispatcher = ((ServletRequest) context.getExternalContext().getRequest())
        .getRequestDispatcher("/j_spring_security_logout");

try {
        dispatcher.forward((ServletRequest) context.getExternalContext().getRequest(),
               (ServletResponse) context.getExternalContext().getResponse());
    } catch (ServletException e) {
        log.error("ServletException", e);
    } catch (IOException e) {
        log.error("IOException", e);
    }

FacesContext.getCurrentInstance().responseComplete();
// It's OK to return null here because Faces is just going to exit.

log.info("End LoginBean.logout");       
return "";
}

public boolean isLoggedIn() {
    FacesContext context = FacesContext.getCurrentInstance();
    Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
    return sessionMap.containsKey("sessionBean");
}

编辑:

我想我现在更好地了解了您的问题。我记得我也很难使它正常工作,所以基本上我必须编写自己的实现AuthenticationFailureHandler的类并正确实现该方法:

@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException, ServletException {
  //Do business logic stuff, logging, etc...
  request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, ex);
  response.sendRedirect("login.xhtml");

[基本上,您看到我正在实例化一个异常并将其设置为会话属性,以便以后可以在我的托管bean中将其检索并转换为FacesMessage

(您还必须将此AuthenticationFailureHandler声明为Spring Security配置文件中的身份验证失败事件的自定义处理程序,或可能也不想这样做)。

<form-login login-page="/login.xhtml" login-processing-url="/j_spring_security_check" authentication-success-handler-ref="authenticationSuccessBean" authentication-failure-handler-ref="authenticationFailureBean" /> ... <beans:bean id="authenticationFailureBean" class="com.maple.controllers.FailureHandler"> <beans:property name="userBo" ref="userController" /> <!-- Just injecting my BL layer... --> </beans:bean>

0
投票
一个简单的解决方案是创建一个实现AuthenticationFailureHandler的spring bean

@Component public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler { @Autowired private ServletContext context; @Override public void onAuthenticationFailure( HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { request.setAttribute("message", exception.getMessage()); RequestDispatcher dispatcher = context.getRequestDispatcher("/login.xhtml"); dispatcher.forward(request, response); } }

然后配置spring security以使用它:

http.formLogin().loginPage("/login.xhtml").permitAll() .failureUrl("/login.xhtml?error=true") .failureHandler(customAuthenticationFailureHandler)

最后,在login.xhtml中,使用此代码显示消息(如果您不使用质数,只需根据您的上下文调整代码):

<p:outputPanel rendered="#{not empty request.getAttribute('message')}" class="ui-messages ui-widget" > <div class="ui-messages-error ui-corner-all"> <span class="ui-messages-error-icon"> </span> <ul> <li role="alert" aria-atomic="true"> <span class="ui-messages-error-summary">#{request.getAttribute('message')}</span> </li> </ul> </div> </p:outputPanel>

© www.soinside.com 2019 - 2024. All rights reserved.