domenica 7 dicembre 2014

Autenticazione sicura con Primefaces

Vai all'indice

L'obiettivo di questo post è creare una pagina di autenticazione sicura con Primefaces. Per fare questo userò la libreria javax.servlet.Filter che protegge le risorse all'interno di un determinato percorso dell'applicazione.
La pagina login.xhtml acquisisce le chiavi di accesso, se le credenziali sono corrette si accede alle pagine home.xhtml ed esempio.xhtml. Se tentiamo di accedere senza autenticazione ad una di queste due pagine, il filtro ci rimanda alla pagina di login.

La costruzione della webapp, del file pom.xml e web.xml è la stessa già vista nel post Primefaces esempio introduttivo, mentre cambia la struttura del faces-config che riporto di seguito:

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.0"
    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">
    <navigation-rule>
        <from-view-id>/login.jsf</from-view-id>
        <navigation-case>
            <from-outcome>home</from-outcome>
            <to-view-id>/home.jsf</to-view-id>
        <redirect>     
        </redirect>
        </navigation-case>
 </navigation-rule>
</faces-config>

Di seguito riporto la pagina di accesso login.xhtml ed il bean associato.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Pagina di login</title>
    </h:head>
    <h:body>
        <h:form id="loginForm">          
            <p:growl id="msg" showDetail="true" life="3000" />
            <p:panel header="Login" style="width: 360px;">
                <h:panelGrid id="loginPanel" columns="2">
                    <h:outputText value="Username" />
                    <p:inputText id="username" value="#{loginBean.uname}" ></p:inputText>
                    <p:spacer></p:spacer> 
                    <p:message for="username" ></p:message>
                    <h:outputText value="Password" />
                    <p:password id="password" value="#{loginBean.password}"  feedback="false"></p:password>
                    <p:spacer></p:spacer>
                    <p:message for="password"></p:message>
                    <p:spacer></p:spacer>
                    <p:commandButton action="#{loginBean.loginProject}" value="Login" update="loginForm" ajax="true"></p:commandButton>
                </h:panelGrid> 
            </p:panel>
        </h:form>
    </h:body> 
</html>

package it.Beans;

import it.Dao.UserDAO;
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpSession;

@ManagedBean(name = "loginBean")
@SessionScoped

public class LoginBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private String password;
    private String message, uname;

    public String loginProject() {
        boolean result = UserDAO.login(uname, password);
        if (result) {
            // Richiama la sessione Http e salva username
            HttpSession session = Util.getSession();
            session.setAttribute("username", uname);

            return "home";
        } else {

            FacesContext.getCurrentInstance().addMessage(
                    null,
                    new FacesMessage(FacesMessage.SEVERITY_WARN,
                    "Credenziali di accesso non valide!",
                    "Riprova di nuovo!"));

            // manda un messaggio di sessione non valida

            return "login";
        }
    }

    public String logout() {
      HttpSession session = Util.getSession();
      session.invalidate();
      return "login";
     }

     // metodi getter e setter
}


La classe LoginBean verifica che le credenziali inserite siano corrette, in tal caso richiama la sessione Http e salva lo username nella sessione corrente. In caso di credenziali errate visualizza un messaggio di errore.

Riporto di seguito le pagine visualizzate dopo la login e protette dal filtro.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Homepage</title>
    </h:head>
    <h:body>
        <h:form>       
            <h1>Benvenuto, #{loginBean.uname}</h1>
            <p>questa è la home page del sito.</p>
            <p:link outcome="esempio" value="Vai alla pagina di esempio"></p:link><br/><br/>
        <p:commandButton action="#{loginBean.logout}" value="Logout"  ajax="false"></p:commandButton>
    </h:form>
    </h:body>
</html>


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Pagina di esempio</title>
    </h:head>
    <h:body>
        <h:form>       
            <h1>Benvenuto, #{loginBean.uname}</h1> 
            <p>questa è una pagina di esempio.</p>
            
            <p:link outcome="home" value="Vai alla homepage"></p:link><br/><br/>
        <p:commandButton action="#{loginBean.logout}" value="Logout"  ajax="false"></p:commandButton>
    </h:form>
    </h:body>
</html>


package it.Dao;
  
public class UserDAO {      
     public static boolean login(String user, String password) {

    if (user.equals("admin") && password.equals("test")) 
            {
               return true;
            }
            else {
                return false;
            }
    }
}

La classe UserDAO simula la chiamata al database per recuperare le chiavi di accesso.

package it.Beans;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class Util {
      public static HttpSession getSession() {
        return (HttpSession)
          FacesContext.
          getCurrentInstance().
          getExternalContext().
          getSession(false);
      }
       
      public static HttpServletRequest getRequest() {
       return (HttpServletRequest) FacesContext.
          getCurrentInstance().
          getExternalContext().getRequest();
      }
}

La classe Util definisce i metodi HttpSession e HttpServletRequest.
HttpServletRequest oltre a permettere l’accesso a tutte le informazioni relative allo header http, permette di ricavare i parametri passati insieme all’invocazione del client.
HttpSession permette di memorizzare le informazioni provenienti dal client all’interno di una sessione salvata sul server al quale viene affidato un identificativo di sessione.

package it.Filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebFilter(filterName = "AuthFilter", urlPatterns = {"*.jsf"})
public class AuthFilter implements Filter {

    public AuthFilter() {
    }
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {   
        try {

            // verifica se la variabile sessione è impostata
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
            res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
            res.setHeader("Pragma", "no-cache"); // HTTP 1.0.
            res.setDateHeader("Expires", 0); // Proxies.
            HttpSession ses = req.getSession(false);
            //  permette all'utente di procedere se ci sono i presupposti
            String reqURI = req.getRequestURI();
            if (reqURI.indexOf("/login.jsf") >= 0 || (ses != null && ses.getAttribute("username") != null)
                    || reqURI.indexOf("/public/") >= 0 || reqURI.contains("javax.faces.resource")) {
                chain.doFilter(request, response);
            } else // utente non loggato ma tenta di accedere ad una pagina non permessa viene rimandato alla pagina di login
            {
                res.sendRedirect(req.getContextPath() + "/login.jsf");  // L'utente anonimo viene rimandato alla pagina di login
            }
        } catch (Throwable t) {
            System.out.println(t.getMessage());
        }    
    
    }
    
    @Override
    public void destroy() {
    }
}

La classe AuthFilter permette all'utente di procedere se ci sono i presupposti e se le credenziali sono corrette, se utente non loggato tenta di accedere alle pagine protette viene rimandato alla pagina di login.
I filtri sono delle classi di utilità molto importanti in quanto consentono di creare un flusso di operazioni particolari senza modificare la struttura principale del nostro software.
Come dimostrato dal nostro esempio anch'essi possono beneficiare delle annotation (vedi @WebFilter), liberandoci dalla necessità di configurare un apposito descrittore nel file web.xml.

Il progetto è ora completo. Lanciando l'applicazione verrà visualizzata la pagina di login:


Anche questo esempio è scaricabile da GitHub.

Hai apprezzato questo post? Conferma le mie competenze o scrivi una segnalazione sul mio profilo Linkedin!

Nessun commento:

Posta un commento