Visualizzazione post con etichetta Il progetto. Mostra tutti i post
Visualizzazione post con etichetta Il progetto. Mostra tutti i post

mercoledì 25 febbraio 2015

Implementazione di una relazione uno-a-molti

Vai all'indice

Il prossimo passo nella costruzione della webapp Prototype è la gestione degli Uffici e l'integrazione con la classe Utente. La struttura delle entità è la stessa proposta nel post Hibernate relazione uno a molti, nel quale si esemplifica la relazione uno a molti tra due tabelle di un database. Nella specifica relazione utenti/uffici ogni utente appartiene ad un solo ufficio, mentre ad ogni ufficio possono appartenere molti utenti.
Per la gestione delle operazioni di inserimento, cancellazione e modifica creo una nuova pagina ufficio.xhtml a cui associo il backing bean ufficiBean. La logica di sviluppo è del tutto simile a quella già descritta per la gestione degli utenti, ma molto più semplice, in quanto l'elaborazione è limitata alla gestione di due campi. Per questo motivo non mi soffermo oltre su questo aspetto, tuttavia chi è interessato può trovare tutto il codice su GitHub.



       <h:selectOneMenu id="ufficio" value="#{utenti.seluff}" effect="fold">
             <f:selectItems value="#{utenti.ufficio1Value}"/>
       </h:selectOneMenu>

Il managed bean utentiBean subisce sostanziali modifiche per la gestione del nuovo campo. Innanzitutto definisco un oggetto Map a cui assegno il nome ufficio1List e nel quale carico i dati da inserire nell'oggetto selectOneMenu. Nel codice seguente riporto il metodo per il caricamento della mappa partendo dall'estrazione della tabella Ufficio:

 public Map ufficio1List = new HashMap();
     public Map getUfficio1Value() {
        int indi=0;
        for (Ufficio uff : uffix) {
             ufficio1List.put(uff.getNomeUfficio(), Integer.toString(uff.getUfficioId()));
             indi = indi + 1;
        }
     return ufficio1List;
  }

Nei metodi addAction e onEdit, che rispondono rispettivamente agli eventi di caricamento e modifica dei dati degli utenti, definisco il collegamento tra gli oggetti Utente e Ufficio.

Ufficio uff = ufficioDao.getUfficio(Integer.parseInt(seluff));

Nell'entità di appoggio utenteBean, che raggruppa i dati estratti dalle tabelle Utente e DettaglioUtente per il caricamento dell'oggetto datatable, definisco il nuovo campo nomeUff in cui salvo la denominazione dell'ufficio da esporre come output al momento del caricamento.

La WebApp aggiornata è scaricabile da GitHub.

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

martedì 3 febbraio 2015

Approfondimenti sulla validazione degli utenti

Vai all'indice

Nel post precedente vi ho esposto come ho definito il controllo accessi per l'applicazione "prototype". Nei prossimi paragrafi vi fornirò alcuni dettagli sulle modifiche all'applicazione utili alla gestione degli utenti loggati. A conclusione dell'articolo mi soffermerò sulle regole di navigazione in JSF.
In testa alla pagina index inserisco un nuovo oggetto toolbar in cui riporto lo username dell'utente loggato e i due pulsanti di logoff e cambio password.







<p:toolbar>
        <f:facet name="left"> 
           <h:outputText value="Utente attivo: #{loginBean.uname}" style="color:#2779BD;"/>
        </f:facet>
        <f:facet name="right">
           <p:commandButton action="#{utenti.changePass}" value="Cambio password"  ajax="false">    
           </p:commandButton>
           <p:commandButton action="#{loginBean.logout}" value="Logout"  ajax="false">     
           </p:commandButton>  
        </f:facet>
</p:toolbar>

La pagina changepass riverifica le credenziali di accesso e richiede un doppio inserimento della nuova password. Una funzione di controllo verifica che i due valori  inseriti coincidano ed esegue un test sulla sicurezza della nuova password.



<p:toolbar>
            <h:form id="loginForm">          
            <p:growl id="msg" showDetail="true" life="3000" />
            <p:panel header="Cambio password" style="width: 360px;">
                <h:panelGrid id="loginPanel" columns="2">
                    <h:outputText value="Username" />
                    <p:inputText id="username" value="#{cambioBean.uname}" ></p:inputText>
                    <h:outputText value="Password" />
                    <p:password id="password" value="#{cambioBean.password}" feedback="false">
                    </p:password>
                    <p:spacer></p:spacer>
                    <p:spacer></p:spacer> 
                   
                    <h:outputLabel for="pwd1" value="Nuova password: " />
                    <p:password id="pwd1" value="#{cambioBean.password1}" match="pwd2" label="Password 1" feedback="true" required="true" />

                    <h:outputLabel for="pwd2" value="Riperti password: " />
                    <p:password id="pwd2" value="#{cambioBean.password2}" label="Password 2" feedback="true" required="true" />
                    <p:spacer></p:spacer>
                    <p:spacer></p:spacer>                   
                    <p:commandButton action="#{cambioBean.modiPass}" value="Cambio password" update="loginForm" ajax="true"></p:commandButton>
                </h:panelGrid> 
            </p:panel>
        </h:form>


Nel backing-bean cambioPass.java inserisco il controllo delle credenziali di accesso, la codifica e l'aggiornamento del valore della nuova password nel database.

    public String modiPass() {
        
        boolean trovato = utenteDao.login(uname, password);
        if (trovato) {
        Utente result = utenteDao.estrlogin(uname, password);
            String hashed = "";
            try {hashed = hspass.createHash(password2);}
            catch(Exception ex) {System.out.println("Anomalia conversione password");}
        result.setPassword(hashed);
            utenteDao.aggUte(result);
            return "index";
        } else {
            FacesContext.getCurrentInstance().addMessage(
                    null,
                    new FacesMessage(FacesMessage.SEVERITY_WARN,
                    "Credenziali di accesso non valide!",
                    "Riprova di nuovo!"));

            return "changepass";
        }
    }


Approfondimento sulle regole di navigazione in JSF.


Il framework JSF fornisce le navigation-rules che permettono di definire la navigazione da una vista all'altra in un'applicazione web. Le regole di navigazione sono definite nel file di configurazione solitamente denominato faces-config.xml, tuttavia è possibile assegnare un altro nome specificando le direttive nel file web.xml.

 <navigation-rule>
        <from-view-id>/login.jsf</from-view-id>
        <navigation-case>
            <from-outcome>index</from-outcome>
            <to-view-id>/index.jsf</to-view-id>
        <redirect>     
        </redirect>
        </navigation-case>
 </navigation-rule>


Il codice specifica che la pagina login verrà redirezionata verso la pagina index.jsf

  <navigation-rule> 
    <from-view-id>/login.jsp</from-view-id> 
    <navigation-case> 
            <from-outcome>index</from-outcome>
            <to-view-id>/index.jsf</to-view-id>
    </navigation-case> 
    <navigation-case> 
            <from-outcome>goodbye</from-outcome>
            <to-view-id>/goodbye.jsf</to-view-id>
    </navigation-case> 
  </navigation-rule> 


Il codice specidica che la pagina login.jsf ha due output associati a due pagine specifiche.

Il modello di navigazione JSF ci permette di utilizzare dei pattern ovvero dei percorsi che terminano con un "*", in questo caso si vuole applicare una regola a tutte le pagine contenute nella cartella pages.

  <navigation-rule> 
    <from-view-id>/pages/*</from-view-id> 
    <navigation-case> 
            <from-outcome>index</from-outcome>
            <to-view-id>/index.jsf</to-view-id>
    </navigation-case> 
    <navigation-case> 
            <from-outcome>goodbye</from-outcome>
            <to-view-id>/goodbye.jsf</to-view-id>
    </navigation-case> 
  </navigation-rule>   


Questa regola di navigazione verrà applicata per ogni pagina contenuta nel percorso /pages/.

Di seguito un esempio di modello di navigazione JSF più articolato.

  <navigation-rule> 
    <from-view-id>/pages/*</from-view-id> 
    <navigation-case> 
            <from-outcome>goodbye</from-outcome>
            <to-view-id>/goodbye.jsf</to-view-id>
    </navigation-case> 
  </navigation-rule> 
  <navigation-rule> 
    <from-view-id>/pages/login.jsf</from-view-id> 
    <navigation-case> 
            <from-outcome>index</from-outcome>
            <to-view-id>/index.jsf</to-view-id>
    </navigation-case>  
  </navigation-rule> 


In questo caso abbiamo due regole di navigazione: la prima generica per tutte le pagine contenute nel percorso /pages/, la seconda diversa per la pagina login.jsf. In questo caso la regola specifica ha la precedenza sulla regola riferita al path generico.

La WebApp aggiornata è scaricabile da GitHub.

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

mercoledì 28 gennaio 2015

Autenticazione di accesso alla WebApp

Vai all'indice

In questo post vi illustrerò come ho inserito l'autenticazione degli accessi all'applicazione presentata nell'articolo precedente. La logica proposta è la stessa già vista nell'esempio: Autenticazione sicura con Primefaces.

Gli utenti abilitati all'ingresso sono gli stessi gestiti nell'applicazione, quindi utilizzerò la tabella Utente per salvare le credenziali di accesso per la login. Nello specifico ricaverò lo username concatenando i campi Cognome e Nome, mentre per la gestione della password inserirò un nuovo campo nella tabella Utente.

Riporto di seguito l'istruzione SQL per la modifica della tabella utente.

ALTER TABLE utente add COLUMN password VARCHAR(255);


Inoltre l'istruzione seguente inizializza il campo password col valore cifrato 'testtest00'.

update utente set password='1000:c5cca4907214bb9fb4d9e978bb970f63875350f3456ea492:49685e1f51d4cdebb3e3c8127ae05264fac87b50d473ea2b';

Per la protezione del campo password utilizzerò un meccanismo di cifratura che che mi permetterà di "cammuffare" l'informazione. Questi metodi sono detti funzioni hash o cifratura a senso unico e sono utilizzati per la cifratura delle password. E' buona norma non memorizzare le password in chiaro sul sistema, ma soltanto la loro cifratura. Quando un utente vuole accedere al sistema, il meccanismo di autenticazione richiede la password, la cifra con l'algoritmo di hashing considerato e confronta la password cifrata con quella memorizzata sul sistema per l'utente in questione. Se le due password cifrate coincidono, all'utente è permesso accedere al sistema.

Dall'esempio Autenticazione sicura con Primefaces importo i file necessari per l'implementazione del controllo accessi.

  • Il file login.xhtml contiene il form per l'autenticazione e viene impostato come welcome-page dell'applicazione, 
  • al bean associato loginBean aggiungo l'interrogazione delle chiavi di accesso nel database e la crittografia per rendere sicure le password degli utenti, 
  • la classe util inserita nel pacchetto it.prototype.utils contiene i metodi HttpSession e HttpServletRequest, 
  • la classe AuthFilter contiene il filtro di autenticazione e va inserito nel pacchetto it.Filters. 

Modifico il file web.xml indicando login.xhtml come nuova welcome-page, mentre nel file faces-config.xml inserisco il tag navigation-rule che definisce le regole di navigazione.
Aggiungo la nuova classe hashpass che contiene l'algoritmo di hashing per la crittografia della password.



Nel prossimo post vedremo come implementare la gestione degli uffici a cui appartengono gli utenti e delle applicazioni a cui essi sono abilitati.

La WebApp aggiornata con il controllo accessi è scaricabile da GitHub.

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

giovedì 22 gennaio 2015

WebApp con Spring Hibernate e Primefaces

Vai all'indice

L'obiettivo di questo post è la realizzazione di un'architettura enterprise e multidatasource costruita integrando i framework Spring, Hibernate e Primefaces. La web application proposta in questo esempio gestisce i dati contenuti nel database Apache Derby già utilizzato in precedenza di cui riporto di seguito la struttura.



Per fare questo riprenderò i concetti visti nei post Introduzione a Spring con esempio pratico e Inserire, cancellare modificare righe su Datatable Primefaces. Nel primo esempio vi ho mostrato come mappare il modello dati e costruire le classi per la gestione delle informazioni provenienti dal database grazie all'utilizzo di Spring e Hibernate, mentre nel secondo abbiamo visto la gestione grafica dell'interfaccia utente con Primefaces.

La costruzione della WebApp


Per prima cosa creo una nuova WebApp mantenendo l'interfaccia già vista nell' esempio precedente, pertanto, dopo avere definito un nuovo progetto Maven, importo la pagina index.xhtml, i bean associati, i file web.xml e faces-context.xml di configurazione.

Dall'esempio Introduzione a Spring con esempio pratico importo i pacchetti contenenti le entità e le classi DAO. Inoltre inserisco il file applicationContext.xml di Spring nel percorso WebContent > WEB-INF.

A questo punto, dopo l'importazione dei moduli di nostro interesse dai due progetti, è necessario integrare la parte di gestione dati con l'interfaccia grafica, riporto di seguito le modifiche salienti ai singoli moduli.
Come prima cosa aggiungo nel web.xml i listener di Spring.

  <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
      /WEB-INF/applicationContext.xml
      </param-value>
  </context-param>
  <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
   </listener>
   <listener>
  <listener-class>
   org.springframework.web.context.request.RequestContextListener
  </listener-class>
   </listener>

La classe ContextLoaderListener è un ServletContextListener, che viene chiamato all’avvio del Tomcat, per inizializzare e caricare il context di Spring nell'applicazione, mentre RequestContextListener serve a Spring per dichiarare lo scope associato al bean.
Grazie al parametro contextConfigLocation è possibile indicare il percorso del file di configurazione del contesto di Spring.

   <application>
     <el-resolver>
      org.springframework.web.jsf.el.SpringBeanFacesELResolver
     </el-resolver>
   </application>

Sia JSF che Spring gestiscono un proprio IoC Container, per cui è necessario trovare un modo per farli comunicare, ed è Spring che se ne occupa. Inserendo nel faces-context.xml di JSF la definizione della classe el-resolver che delega a Spring tutte le espressioni EL che JSF non riesce a risolvere, tra cui gli stessi bean definiti nel container di Spring.

  <dependency>
     <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.16</version>
  </dependency>
  <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>1.5.6</version>
  </dependency>
  <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-log4j12</artifactId>
     <version>1.5.6</version>
  </dependency>


Nel pom.xml inserisco tutte le dipendenze di Spring, Hibernate e Primefaces già viste nei due esempi sopra citati. Inoltre ho aggiunto la dipendenza c3p0 per la gestione del pool di connessione e le dipendenze slf4j e log4j per inserire nel progetto la gestione di un sistema di logging.

@ManagedBean(name = "utenti")
@RequestScoped
public class utentiBean implements Serializable {

    private static final long serialVersionUID = 1L;
    public int userid;
    public String nome;
    public String cognome;
    public String ruolo;
    private Date data;
    public String via;
    public String citta;
    public String telefono;
    public List<Utente> users;
utenteBean utente;
    
    @ManagedProperty(value="#{utenteDao}")
    UtenteDao utenteDao;
    
    // Definizione Getter e Setter

    private static final ArrayList<utenteBean> utentiList = new ArrayList<utenteBean>();

    public ArrayList<utenteBean> getUtentiList() {
        return utentiList;
    }
    
    @PostConstruct
    public void init() {
   users = utenteDao.getAll();
   inizializza();
   initdata();
}

    public String addAction() {
   
     // Istanzio e salvo gli oggetti utente e dettaglioutente
     Dettaglioutente dettUte = new Dettaglioutente(this.data, this.via, this.citta, this.telefono);
     Utente ute = new Utente(this.nome, this.cognome, this.ruolo);
     utenteDao.saveDetUte(ute, dettUte);
   
     inizializza();

     nome = "";
     cognome = "";
     ruolo = "";
     via = "";
     citta = "";
     telefono = "";
     initdata();
     return null;
    }
    
    public void onEdit(RowEditEvent event) { 
        Dettaglioutente dettUte = new Dettaglioutente(((utenteBean) event.getObject()).getUserid(), ((utenteBean) event.getObject()).getData(), ((utenteBean) event.getObject()).getVia(), ((utenteBean) event.getObject()).getCitta(), ((utenteBean) event.getObject()).getTelefono());
        Utente ute = new Utente(((utenteBean) event.getObject()).getUserid(), ((utenteBean) event.getObject()).getNome(), ((utenteBean) event.getObject()).getCognome(), ((utenteBean) event.getObject()).getRuolo());  
        // Aggiorno l'utente
        utenteDao.aggUte(ute, dettUte);
        inizializza();
        FacesMessage msg = new FacesMessage("Record modificato",((utenteBean) event.getObject()).getNome());  
        FacesContext.getCurrentInstance().addMessage(null, msg);  
    }  
       
    public void onCancel(RowEditEvent event) {  
        FacesMessage msg = new FacesMessage("Modifiche annullate");   
        FacesContext.getCurrentInstance().addMessage(null, msg); 
    } 
    
public void delete(utenteBean std){ 
// Elimina l'utente
        utenteDao.deleteUtente(std.getUserid());
        inizializza();
FacesMessage msg = new FacesMessage("Record cancellato");   
        FacesContext.getCurrentInstance().addMessage(null, msg);
}

public void inizializza(){
        utentiList.clear();
   List<Utente> users = utenteDao.getAll();
for (Utente user : users) {
Dettaglioutente dettuser = utenteDao.getDettaglioutente(user.getuserId());
utenteBean utentetmp = new utenteBean(user.getuserId(), user.getNome(), user.getCognome(), user.getRuolo(), dettuser.getDataNascita(), dettuser.getVia(), dettuser.getCitta(), dettuser.getTelefono());
       utentiList.add(utentetmp);
}
}

public void initdata(){
    String dateStr = "1970-01-01T00:00:00.000+01:00";
    SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ"); 
    try  {
    data = sdf.parse(dateStr.replaceAll(":(?=..$)", "")); 
    } catch (ParseException e) { 
        System.out.println("Anomalia parser data " + sdf); 
    }
}



UtentiBean è la classe di gestione della pagina index.xhtml ed è il modulo chiave su cui agire per collegare la parte di presentazione con la gestione dei dati.
L'annotazione @managedbean è usata per configurare l’iniezione di dipendenza in Spring ed è associata alla classe UtenteDao che contiene i metodi di interfaccia per l'accesso alle tabelle utente.
@PostContruct è l'annotazione applicata al metodo init() per indicare che deve essere chiamato dopo che tutte le dependency injection sono state completate. Nel nostro caso specifico è utilizzata per caricare i campi del Datatable coi dati estratti dal database.
Le chiamate ai metodi della gestione dei dati vengono attivate tramite gli eventi di inserimento, cancellazione e modifica dei dati presenti nel Datatable di Primefaces.
A questo punto la stesura del codice è terminata ed è possibile testare l'applicazione.



L'interfaccia grafica gestisce i dati relativi agli utenti ed è formata da un'unica pagina web divisa in due schede. La scheda inferiore contiene il modulo di inserimento nuovo utente, mentre la scheda superiore contiene un oggetto Datatable che permette la visualizzazione, l'aggiornamento e la cancellazione dei dati inseriti. Poichè a livello di Database i dati relativi agli utenti sono divisi in 2 tabelle (utente e dettaglioutente tra cui esiste una relazione uno a uno) ma l'oggetto datatable è unico, per comodità ho mappato i dati nel POJO UtenteBean prima di caricarli nel Datatable.

Nei prossimi post vedremo come implementare e completare il progetto appena iniziato.

Anche questo esempio è scaricabile da GitHub.

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