I was trying to create a secure login page with jsf, and I used these code snippets as the solution, found in this question. My problem is, that I can access the /restricted/secret.xhtml without logging in, there is no redirect it's like the filter is not applied, because if I go directly to the /restricted/secret.xhtml the #{user.loggedIn} evaluates to false and I still can view the page. Here is my code:
AuthFilter.java
public class AuthFilter implements Filter {
private FilterConfig config;
@Override
public void destroy() {
this.config = null;
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain ch) throws IOException, ServletException {
HttpSession s = ((HttpServletRequest) req).getSession();
if (s.getAttribute(UserBean.CREDENTIAL)==null)
{
((HttpServletResponse) resp).sendRedirect("/login.faces");
}else
{
ch.doFilter(req, resp);
}
}
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
}
UserBean.java
@ManagedBean(name="user")
@SessionScoped
public class UserBean implements Serializable {
private String name;
private String password;
protected static final String CREDENTIAL = "ontherun";
private static final long serialVersionUID = 1L;
public String getName()
{
return this.name;
}
public void setName(String newName)
{
this.name = newName;
}
public String getPassword()
{
return this.password;
}
public void setPassword(String newPassword)
{
this.password = newPassword;
}
public boolean isLoggedIn()
{
return FacesContext.getCurrentInstance().getExternalContext()
.getSessionMap().get(CREDENTIAL) != null;
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(CREDENTIAL);
return null;
}
public String login()
{
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(CREDENTIAL, this.name);
return "secret";
}
}
Here is my login.xhtml ; the page works correctly, so there is no problem with the template file.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<head><title>IGNORED</title></head>
<body>
<ui:composition template="/templates/masterLayoutTemplate.xhtml">
<ui:define name="windowTitle">
#{msgs.window_title}
</ui:define>
<ui:define name="header">
<ui:include src="/sections/login/header.xhtml"></ui:include>
</ui:define>
<ui:define name="footer">
<ui:include src="/sections/login/footer.xhtml"></ui:include>
</ui:define>
<ui:define name="content">
<h:form>
<h:panelGrid columns="2">
#{msgs.namePrompt}
<h:inputText id="name" value="#{user.name}"/>
#{msgs.passwordPrompt}
<h:inputSecret id="password" value="#{user.password}"/>
</h:panelGrid>
<p>
<h:commandButton value="#{msgs.loginButtonText}" action="#{user.login }"/>
</p>
<p>
You are logged in : #{user.loggedIn}
</p>
<p>
<h:commandButton value="logout" action="#{user.logout }"/>
</p>
</h:form>
</ui:define>
</ui:composition>
</body>
</html>
Here is the secret.xhtml which is supposed to be restricted:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<head><title>IGNORED</title></head>
<body>
<ui:composition template="/templates/masterLayoutTemplate.xhtml">
<ui:define name="windowTitle">
#{msgs.window_title}
</ui:define>
<ui:define name="content">
<h:head></h:head>
<h:body>
<p>You are #{user.loggedIn}</p>
</h:body>
</ui:define>
</ui:composition>
</body>
</html>
And here are my config files: web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>OnTheRun</display-name>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>on.run.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/restricted/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
</web-app>
and faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<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_2_0.xsd"
version="2.0">
<application>
<resource-bundle>
<base-name>on.run.messages</base-name>
<var>msgs</var>
</resource-bundle>
</application>
<navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-outcome>login</from-outcome>
<to-view-id>/profile.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/login.xhtml</from-view-id>
<navigation-case>
<from-outcome>secret</from-outcome>
<to-view-id>/restricted/secret.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
</faces-config>
My directory structure looks like this: dirStruct 2
You've mapped FacesServlet
on /faces/*
instead of *.xhtml
. So all JSF requests will have the /faces
prefix in the URL. But you've mapped the AuthFilter
on /restricted/*
instead of /faces/restricted/*
, so it will never kick in on /faces/*
URLs.
You can solve this in 2 ways:
Map FacesServlet
on *.xhtml
instead of on /faces/*
. This has the additional advantage that the enduser won't ever be able to see the raw JSF source code when the enduser purposefully removes the /faces
path from the URL in browser address bar.
Map AuthFilter
on /faces/restricted/*
instead of on /restricted/*
.
I personally recommend the first way. You end up with a shorter and nicer URL and you immediately also prevent the JSF source code leak.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With