第 45 章 在JSF中使用Spring Security

在网上看到一个同志说不知道如何在JSF中使用Spring Security,这里特别做了一个例子演示一下,预先声明一下咱们对JSF并不太了解,例子略显简单,将就着用吧。

45.1. 修改过滤器支持forward

第一步就是修改web.xml中的过滤器配置,这样才能支持forward,否则默认只支持request方式的请求。

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>
        

45.2. 自定义登录页面

使用JSF写一个登陆页面。

<f:view>
    <h:messages />
    <h:form
        id="loginForm"
        prependId="false">
        <label for="j_username"><h:outputText value="Username:" /><br />
        </label>
        <h:inputText
            id="j_username"
            required="true">
        </h:inputText>

        <br />
        <br />
        <label for="j_password"><h:outputText value="Password:" /><br />
        </label>
        <h:inputSecret
            id="j_password"
            required="true">
        </h:inputSecret>

        <br />
        <br />
        <label for="_spring_security_remember_me"> <h:outputText
            value="Remember me" /> </label>
        <h:selectBooleanCheckbox id="_spring_security_remember_me" />
        <br />

        <h:commandButton
            type="submit"
            id="login"
            action="#{loginBean.doLogin}"
            value="Login" />

    </h:form>
</f:view>
        

然后创建对应的loginBean。

public String doLogin() throws IOException, ServletException {
    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.
    return null;
}
        

它主要负责在进行doLogin时,将请求转发到/j_spring_security进行用户登录认证。

45.3. 显示密码错误信息

为了在自定义页面回显密码错误的信息,需要定义一个监听器。

package com.family168.springsecuritybook.ch213;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;


public class LoginErrorPhaseListener implements PhaseListener {
    private static final long serialVersionUID = -1216620620302322995L;

    public void beforePhase(final PhaseEvent arg0) {
        Exception e = (Exception) FacesContext.getCurrentInstance()
                                              .getExternalContext()
                                              .getSessionMap()
                                              .get(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY);

        if (e instanceof BadCredentialsException) {
            FacesContext.getCurrentInstance().getExternalContext()
                        .getSessionMap()
                        .put(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY,
                null);
            FacesContext.getCurrentInstance()
                        .addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Username or password not valid.", null));
        }
    }

    public void afterPhase(final PhaseEvent arg0) {
    }

    public PhaseId getPhaseId() {
        return PhaseId.RENDER_RESPONSE;
    }
}
        

在解析之前判断是否存在BadCredentialsException异常,如果存在,就添加一条message,这条message会显示在登录页面上。

在faces-config.xml添加这个监听器即可生效。

<?xml version="1.0"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2"
>
    <lifecycle>
        <phase-listener>com.family168.springsecuritybook.ch213.LoginErrorPhaseListener</phase-listener>
    </lifecycle>
</faces-config>

        

实例在ch213中。