JSF Spring Security сообщение об ошибке входа в систему - PullRequest
3 голосов
/ 19 января 2012

Я использую Spring Security 3 в своем веб-приложении jsf2.

Как я могу показать сообщение с неверными учетными данными в форме входа без добавления параметра get? Login_error на страницу authenticated-fail-login-page?

Я пытался использовать фазовый слушатель, как показано в этом уроке:

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

Но это не работает.

Ни сслушатель preRenderView.

И не проверять последнее исключение безопасности пружины для рендеринга сообщения.

Есть идеи?

ОБНОВЛЕНИЕ:

Моя страница входа в систему:

<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>

Мой управляемый компонент:

@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>

1 Ответ

7 голосов
/ 19 января 2012

Я склонен использовать <f:event> в preRenderView, который будет обновлять компонент сообщений в моей форме.Вот как я это сделал.

<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 я перенаправляю запрос на сервлет Spring Security и обновляю сообщения.Вы заметите, что у меня также есть код для действия по выходу из системы, если вам интересно узнать, как я подошел к этой проблеме.

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");
}

РЕДАКТИРОВАТЬ:

Iдумаю, я лучше понимаю вашу проблему сейчас.Я помню, что у меня были проблемы с тем, чтобы заставить это работать, поэтому в основном мне пришлось написать свой собственный класс, который реализует 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");

В основном вы видите, что я создаю исключениеи установив его в качестве атрибута сеанса, чтобы позже в моем управляемом компоненте его можно было извлечь и преобразовать в 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...