I'm trying to use HttpServletRequest authenticate within a JSF managed bean to implement fine-grained authentication, depending on the particular object requested.
When I call authenticate within a preRenderView event listener, if authenticate causes a redirect to the login page, an exception is thrown. I can't call responseComplete after the call to authenticate, because FacesContext.getCurrentInstance returns null. Is it possible to call authenticate() at all in JSF, or do I have to use a ServletFilter? HttpServletRequest login and logout work within JSF, so I think it's reasonable to assume authenticate should work. Is this a bug in Mojarra JSF?
Here is my code:
The page where event listener is registered:
<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>
The bean that contains the listener:
@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.print开发者_运维百科StackTrace();
}
}
}
The call to authenticate() doesn't throw an exception, but if it returns false, then FacesContext.getCurrentInstance() also returns null, and after the method exits I get this error:
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 )
Thanks, Ellen
From HttpServletRequest#authenticate()
javadoc:
boolean authenticate(HttpServletResponse response) throws java.io.IOException, ServletException
Use the container login mechanism configured for the
ServletContext
to authenticate the user making this request.This method may modify and commit the argument
HttpServletResponse
.
(emphasis mine)
The container's implementation will redirect the response to the configured login page when the user has not been authenticated. This has likely also caused the HttpServletRequest
to be destroyed and thus the current FacesContext
instance to be completely disappeared.
Your best bet is to let JSF perform the checking the presence of the logged-in user and doing the redirect instead of the container. True, this requires duplicating the login page location in the JSF side, but I don't see other ways which are workable in JSF without moving the task to a Filter
.
public void preRender(CompenentSystemEvent event) {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
if (ec.getUserPrincipal() == null) {
ec.redirect(ec.getRequestContextPath() + "/login.xhtml");
}
}
The ExternalContext#redirect()
will already implicitly call FacesContext#responseComplete()
, so you don't need to do it yourself.
精彩评论