在JSF生命周期中使用HttpServletRequest authenticate()的问题

时间:2011-08-31 18:32:18

标签: security jsf-2 java-ee-6 servlet-3.0

我正在尝试在JSF托管bean中使用HttpServletRequest身份验证来实现细粒度身份验证,具体取决于所请求的特定对象。

当我在preRenderView事件侦听器中调用authenticate时,如果authenticate导致重定向到登录页面,则会引发异常。在调用authenticate之后我无法调用responseComplete,因为FacesContext.getCurrentInstance返回null。是否可以在JSF中调用authenticate(),还是必须使用ServletFilter? HttpServletRequest在JSF中登录和注销工作,所以我认为假设认证应该有效是合理的。这是Mojarra JSF中的错误吗?

这是我的代码:

注册事件监听器的页面:

<ui:composition template="/template.xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
 xmlns:h="http://java.sun.com/jsf/html"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:ice="http://www.icesoft.com/icefaces/component">
<ui:param name="pageTitle" value="Text Clustering Home Page"/>
<ui:define name="metadata">
<f:metadata>

   <f:event type="preRenderView" listener="#{permissionBean.preRender}"/>
</f:metadata>
</ui:define>
<ui:define name="body">

      Text Clustering Home Page
      <h:form>
          <h:panelGrid columns="1">
              <ice:outputText rendered="#{loginService.loggedIn}" value="Logged in User: #{loginService.currentUser.username}"/>
              <h:link rendered="#{!loginService.loggedIn}" value="Register" outcome="Register"/>

              <h:commandLink value="Logout" rendered="#{loginService.loggedIn}" action="#{loginService.logout}"/>
              <h:link value="Login" rendered="#{!loginService.loggedIn}" outcome="Login"/>
          </h:panelGrid> 
      </h:form>

包含侦听器的bean:

@Named
@RequestScoped
public class PermissionBean implements java.io.Serializable {

public void preRender(CompenentSystemEvent event) {
    HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
  HttpServletResponse response = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
  try {

      if (!request.authenticate(response)) {
          System.out.println("After authenticate, context = " +FacesContext.getCurrentInstance());
          if (FacesContext.getCurrentInstance()!=null) {                   
            FacesContext.getCurrentInstance().responseComplete();
          }
       }          

  } catch (Exception e) {  // may throw ServletException or IOException
      System.out.println("EXCEPTION calling authenticate");                
      e.printStackTrace();
  }                
}

}

对authenticate()的调用不会抛出异常,但如果返回false,则FacesContext.getCurrentInstance()也返回null,并在方法退出后 我收到这个错误:

javax.enterprise.resource.webcontainer.jsf.context|_ThreadID=23;_Thread
Name=Thread-3;|Exception when handling error trying to reset the
response.
java.lang.NullPointerException
at
com.sun.faces.facelets.tag.jsf.core.DeclarativeSystemEventListener.processEvent(EventHandler.java:126    )

谢谢, 埃伦

1 个答案:

答案 0 :(得分:1)

来自HttpServletRequest#authenticate() javadoc

boolean authenticate(HttpServletResponse response)
                     throws java.io.IOException,
                            ServletException
     

使用为ServletContext配置的容器登录机制对发出此请求的用户进行身份验证。

     

此方法可能会修改并提交参数HttpServletResponse

(强调我的)

当用户未经过身份验证时,容器的实现会将响应重定向到已配置的登录页面。这可能也导致HttpServletRequest被破坏,因此当前FacesContext实例完全消失。

最好的办法是让JSF执行检查已登录用户的存在并执行重定向而不是容器。没错,这需要在JSF方面复制登录页面位置,但是我没有看到在JSF中可行的其他方法而不将任务移动到Filter

public void preRender(CompenentSystemEvent event) {
    ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();

    if (ec.getUserPrincipal() == null) {
        ec.redirect(ec.getRequestContextPath() + "/login.xhtml");
    }
}

ExternalContext#redirect()已隐式调用FacesContext#responseComplete(),因此您无需自行执行此操作。