Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Basic Security in JSF

I would like to see a simple Login Application, not as simple as this though.

What I would like to achieve is an understanding on how JSF works, I've developed a lot of ASP.NET where you have the code behind and where you can just check if a session was created upon Login.

A similar solution in JSF would be great.

This is basically what I want to achieve:

  • Login page
  • IF OK
    • Create session and return "success"
  • IF FAIL
    • return "failure"

(The "success" and failure are mapped to faces-config.xml)

At the success-page I want to be Certain that the user is logged in, so one should Not be able to navigate to "success.jspx" if you have not got the correct session.

like image 822
Filip Ekberg Avatar asked Sep 24 '09 09:09

Filip Ekberg


4 Answers

There is no inherent authentication functionality in core JSF beyond being able to use things like component rendered attributes geared towards role-based security.

By default, a JSF application relies on the same container-managed security mechanisms as the web component that contains it (JEE5 tutorial). 3rd party frameworks like Seam can provide alternatives.

If you want to add your own application security, a servlet filter is one of the simpler mechanisms.

This filter protects resources under the restricted directory as defined in web.xml:

  <filter>
    <filter-name>AuthenticationFilter</filter-name>
    <filter-class>restricted.AuthenticationFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>AuthenticationFilter</filter-name>
    <url-pattern>/restricted/*</url-pattern>
  </filter-mapping>

The filter class implementation:

public class AuthenticationFilter implements Filter {
  private FilterConfig config;

  public void doFilter(ServletRequest req, ServletResponse resp,
      FilterChain chain) throws IOException, ServletException {
    if (((HttpServletRequest) req).getSession().getAttribute(
        AuthenticationBean.AUTH_KEY) == null) {
      ((HttpServletResponse) resp).sendRedirect("../restricted_login.faces");
    } else {
      chain.doFilter(req, resp);
    }
  }

  public void init(FilterConfig config) throws ServletException {
    this.config = config;
  }

  public void destroy() {
    config = null;
  }
}

A login bean defined in faces-config.xml:

public class AuthenticationBean {
  public static final String AUTH_KEY = "app.user.name";

  private String name;
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }

  public boolean isLoggedIn() {
    return FacesContext.getCurrentInstance().getExternalContext()
        .getSessionMap().get(AUTH_KEY) != null;
  }

  public String login() {
    FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
        AUTH_KEY, name);
    return "secret";
  }

  public String logout() {
    FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
        .remove(AUTH_KEY);
    return null;
  }
}

The JSF login form in the restricted_login.jsp page:

  <f:view>
    <p><a href="restricted/secret.faces">try to go to secret
    page</a></p>
    <h:form>
    Username:
    <h:panelGroup rendered="#{not authenticationBean.loggedIn}">
        <h:inputText value="#{authenticationBean.name}" />
        <h:commandButton value="login"
          action="#{authenticationBean.login}" />
      </h:panelGroup>
      <h:commandButton value="logout"
        action="#{authenticationBean.logout}"
        rendered="#{authenticationBean.loggedIn}" />
    </h:form>
  </f:view>

(The redirect URL/mechanism was chosen for brevity rather than any sort of best practice; see the Servlet API for more options.)

like image 95
McDowell Avatar answered Oct 20 '22 05:10

McDowell


If you're willing to try a bit more advanced approach then I suggest looking into spring-security+JSF. It works like a charm.

You can write your application as if it wasn't under security and then just configure which areas that should be protected using aspects.

Spring security: http://static.springsource.org/spring-security/site/

A Tutorial: http://ocpsoft.com/java/acegi-spring-security-jsf-login-page/

like image 33
Tomas F Avatar answered Oct 20 '22 05:10

Tomas F


The best way to do it would be to use container managed security.

Here is a tutorial on how to achieve that with glassfish and jsf.

like image 3
gordan Avatar answered Oct 20 '22 05:10

gordan


If you use templates, I've found you don't really need a filter.

index.jsp

<jsp:forward page="startup.faces"></jsp:forward>

startup.xhtml (.faces), doesn't actualy try to show a screen, it calles the javascript startupSubmit() on load and that clicks the button. This sends the flow straight to the method start() in StartupBean.java.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0     Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
.
.
 <script type="text/javascript">
 function startupSubmit() {
  **document.getElementById('startupForm:startupBtn').click();**
 }
 </script>
 <h:body onload="startupSubmit()">
 <h:form id="startupForm">
 <p:commandButton id="startupBtn" value="" action="#{startupBean.start}" ajax="false" />  
 </h:form>
 </h:body>
</html>

StartupBean.java (is not part of the template.xhtml below). The start() method in StartupBean sets a variable called authorized to true (it defaults to false), then jumps to first.xhtml. You can use any criteria you desire to determine if authorized is set to true... such as login criteria.

package gov.irs.eservices.managementBeans;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name="startupBean")
@SessionScoped
public class StartupBean {

private boolean authorized;

public StartupBean() {
}

public String start() {
    **setAuthorized(true);**
    return "first";
}

public boolean isAuthorized() {
    return authorized;
}

public void setAuthorized(boolean authorized) {
    this.authorized = authorized;
}
}

template.xhtml. In template.xhtml, just inside the form, you place a h: or p: panelGrid and only render it if startupBean.authorized is true. The only way a user can get to the pages contained in the template is if they come through StartupBean.java first.

<f:view>
<div id="container">
<h:form id="templateForm">
**<p:panelGrid rendered="#{startupBean.authorized}">**
    <div id="header">
        <ui:include src="header.xhtml" />
    </div>

    <div id="wrapper">
        <div id="firstId">
            <ui:insert name="first"></ui:insert>
        </div>
.
.  <!-- MORE PAGES -->
.
.
    </div>

    <div id="footer">
        <ui:include src="footer.xhtml" />
    </div>
</p:panelGrid>
</h:form>
</div>      
</f:view>

So, that's my solution. I've tested it pretty thoroughly and it seems to work fine.

like image 2
DougMH Avatar answered Oct 20 '22 05:10

DougMH