Nel capitolo precedente, abbiamo creato dei file che permettono di modificare il contenuto del nostro sito. Ora dobbiamo impedire che a queste pagine possa accedere qualsiasi visitatore. E' una buona occasione per inserire un sistema di autenticazione utenti: solamente chi è a conoscenza di uno username e di una password potrà accedere alle pagine inserimento.cfm, aggiornamento.cfm ed elimina.cfm.
Il funzionamento di un sistema simile può essere schematizzato come in figura.
Quando è chiesta una pagina protetta, deve essere eseguito un controllo che verifichi l'autenticazione dell'utente. Se l'utente non è autenticato, deve essere visualizzato un form per eseguire il login (un modulo che permetta l'inserimento di uno username e di una password); altrimenti la pagina può essere visualizzata.
Come possiamo riconoscere se un visitatore è autenticato? Come abbia detto, ColdFusion è in grado di distinguere il visitatore grazie alle sessioni: ogni client (e, quindi, ogni visitatore) che utilizza l'applicazione rappresenta una e una sola sessione. Il CFML mette a disposizione un set di tag e di funzioni per creare un sistema di autenticazione basato sulle sessioni:
TAG/FUNZIONE | SIGNIFICATO |
<cflogin> |
All'interno del tag <cflogin> si inserisce il codice che deve essere eseguito quando l'utente non è autenticato. Ad esempio, inseriremo il codice per controllare se username e password sono corretti ed, eventualmente, autenticare il visitatore o visualizzare il modulo di login. |
<cfloginuser> | Il tag <cfloginuser> si utilizza in compagnia con <cflogin> e associa username e password ad una sessione. |
<cflogout> | Al contrario, <cflogout> fa "dimenticare" a ColdFusion l'autenticazione della sessione così da considerare l'utente non più autenticato. |
GetAuthUser() | Questa funzione permette di accedere alle informazioni dell'utente autenticato. |
IsUserInRole() | Questa funzione controlla se l'utente autenticato appartiene ad un certo "ruolo". |
E' possibile assegnare ruoli agli utenti per determinare, ad esempio, cosa possono e cosa non possono fare. Così, per il nostro sistema di autenticazione decidiamo di creare diversi tipi di utenti per permettere ad essi di accedere, ad esempio, alla pagina di inserimento ma non di eliminazione dei record.
Ipotizziamo di voler dare l'accesso alle nostre pagine di gestione a quattro utenti diversi. A ciascuno di essi associamo uno username, una password e uno o più ruoli:
Username | Password | Ruoli |
marco | coldfusion | inserimento,modifica |
andrea | discoteca | inserimento |
alessandro | macromedia | modifica |
giampaolo | dreamweaver | inserimento,modifica,eliminazione |
L'utente con ruolo "inserimento" potrà accedere alla pagina inserimento.cfm; quello con ruolo "modifica" alla pagina aggiornamento.cfm e infine il ruolo "eliminazione" sarà per la pagina elimina.cfm. La tabella LOGIN del nostro database replica esattamente questa struttura di utenti.
E' necessaria una piccola osservazione: la "logica" che gestisce l'autenticazione degli utenti conviene inserirla in un file separato. Se ad essa è adibito il compito di controllare se l'utente è autenticato o no, basterà includere questo file in ogni pagina che vogliamo proteggere.
1. Crea un nuovo file chiamato modulo_login.cfm che contiene un semplice form per eseguire l'autenticazione:
<!--- modulo_login.cfm: mostra un modulo per eseguire il login --->.
<p class="titolo">
Esegui l'autenticazione
</p>
<p>
<cfform>
<table border="0" cellspacing="1" cellpadding="3">
<tr>
<td><p>Username:</p></td>
<td><cfinput name="username" type="text" required="yes" message="Inserisci il tuo username." ></td>
</tr>
<tr>
<td><p>Password:</p></td>
<td><cfinput name="password" type="password" ></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="Login"></td>
</tr>
</table>
</cfform>
</p>
Nota che non abbiamo dato alcun "action" al tag <cfform>, per fare in modo che il form punti sempre alla stessa pagina in cui è richiamato.
2. Crea un nuovo file autenticazione.cfm che conterrà la logica del sistema di autenticazione. Con un <cfif> controlliamo se è stato inviato il form di login:
<!--- Il codice all'interno di <cflogin> viene eseguito solo se l'utente non è già autenticato --->.
<cflogin> <cfif isDefined('form.username') AND isDefined('form.password')> <!--- interroga la tabella LOGIN del database per vedere se username e password corrispondono, ed eventualmente eseguire il login --->. <cfelse> <!--- visualizza il form per l'autenticazione contenuto in modulo_login.cfm --->. </cfif></cflogin>
In <cfif> abbiamo utilizzato l'operatore logico AND per verificare che sia definite entrambe le variabili form. Gli operatori logici utilizzabili nel CFML sono NOT, AND, OR, XOR, EQV e IMP.
3. Se il modulo non è stato inviato, allora deve essere incluso il file modulo_login.cfm. Non solo, dopo questo file la pagina ColdFusion deve essere interrotta:
<!--- Il codice all'interno di <cflogin> viene eseguito solo se l'utente non è già autenticato --->.
<cflogin> <cfif isDefined('form.username') AND isDefined('form.password')> <!--- interroga la tabella LOGIN del database per vedere se username e password corrispondono, ed eventualmente eseguire il login --->. <cfelse> <!--- visualizza il form per l'autenticazione contenuto in modulo_login.cfm --->.
<cfinclude template="modulo_login.cfm"> <!--- interrompe l'elaborazione della pagina ColdFusion --->.
<cfinclude template="OnRequestEnd.cfm">
<cfabort> </cfif></cflogin>
Con il tag <cfabort> tutto il codice CFML che segue non viene più elaborato, compreso OnRequestEnd.cfm. Per questo motivo, prima di esso abbiamo incluso il file per visualizzare il piede della pagina.
4. Inserisci ora la query per eseguire un SELECT nella tabella LOGIN filtrando per lo username e la corrispondente password provenienti dal form.
<!--- Il codice all'interno di <cflogin> viene eseguito solo se l'utente non è già autenticato --->.
<cflogin> <cfif isDefined('form.username') AND isDefined('form.password')> <!--- interroga la tabella LOGIN del database per vedere se username e password corrispondono, ed eventualmente eseguire il login --->
<cfquery name="getUtente" datasource="#application.DSN#">
SELECT USERNAME, RUOLI
FROM LOGIN
WHERE USERNAME = '#form.username#' AND PASSWORD = '#form.password#'
</cfquery>
<cfelse> <!--- visualizza il form per l'autenticazione contenuto in modulo_login.cfm --->.
<cfinclude template="modulo_login.cfm"> <!--- interrompe l'elaborazione della pagina ColdFusion --->.
<cfinclude template="OnRequestEnd.cfm">
<cfabort> </cfif></cflogin>
Se username e password esistono e corrispondono, la query restituirà un record. In questo caso la variabile getUtente.RecordCount sarà uguale a 1 e potremo eseguire l'autenticazione. Altrimenti, dovremo mostrare nuovamente il modulo di login con un messaggio che lo username e la password inseriti non corrispondono.
5. Dopo aver verificato che la query getUtente restituisce un record, utilizza il tag <cfloginuser> per eseguire l'autenticazione dell'utente.
<!--- Il codice all'interno di <cflogin> viene eseguito solo se l'utente non è già autenticato --->.
<cflogin> <cfif isDefined('form.username') AND isDefined('form.password')> <!--- interroga la tabella LOGIN del database per vedere se username e password corrispondono, ed eventualmente eseguire il login --->
<cfquery name="getUtente" datasource="#application.DSN#">
SELECT USERNAME, PASSWORD, RUOLI
FROM LOGIN
WHERE USERNAME = '#form.username#' AND PASSWORD = '#form.password#'
</cfquery> <!--- se username e password corrispondono... --->.
<cfif getUtente.RecordCount EQ 1>
<!--- ... autentica l'utente --->.
<cfloginuser name="#getUtente.USERNAME#" password="#getUtente.PASSWORD#" roles="#getUtente.RUOLI#"> <!--- ... altrimenti mostra nuovamente il modulo per il login --->.
<cfelse>
<p>Lo username e la password inseriti non esistono.</p>
<!--- visualizza il form per l'autenticazione contenuto in modulo_login.cfm --->.
<cfinclude template="modulo_login.cfm"> <!--- interrompe l'elaborazione della pagina ColdFusion --->.
<cfinclude template="OnRequestEnd.cfm">
<cfabort>
</cfif>
<cfelse> <!--- visualizza il form per l'autenticazione contenuto in modulo_login.cfm --->.
<cfinclude template="modulo_login.cfm"> <!--- interrompe l'elaborazione della pagina ColdFusion --->.
<cfinclude template="OnRequestEnd.cfm">
<cfabort> </cfif></cflogin>
Il tag <cfloginuser> dice a ColdFusion di considerare la sessione come un utente autenticato. A questo utente il tag assegna un nome, una password e dei ruoli attraverso gli attributi "name", "password" e "roles".
Grazie all'utilizzo di <cfabort>, tutto ciò che segue la chiusura del tag </cflogin> è interpretato da ColdFusion solo se l'utente è autenticato. Possiamo quindi utilizzare la funzione GetAuthUser() per visualizzare il nome dell'utente e dargli un messaggio di benvenuto.
6. Utilizza la funzione GetAuthUser() per mostrare un messaggio di benvenuto:
<!--- Il codice all'interno di <cflogin> viene eseguito solo se l'utente non è già autenticato --->.
<cflogin> <cfif isDefined('form.username') AND isDefined('form.password')>........ </cfif></cflogin><cfoutput>
<p>Bentornato <b>#GetAuthUser()#</b>!</p>
</cfoutput>
7. Aggiungi un form con un pulsante per eseguire il logout. Dai a questo pulsante un nome, ad esempio "faiLogout":
<!--- Il codice all'interno di <cflogin> viene eseguito solo se l'utente non è già autenticato --->.
<cflogin> <cfif isDefined('form.username') AND isDefined('form.password')>........ </cfif></cflogin><cfoutput>
<p>Bentornato <b>#GetAuthUser()#</b>!
<form method="post"><input type="submit" value="Logout" name="faiLogout"></form>
</p>
</cfoutput>
Quando l'utente clicca su questo pulsante, viene inviata una variabile form.faiLogout.
8. All'inizio del file autenticazione.cfm, controlla se tale variabile è definita. Se lo è, utilizza il tag <cflogout> per annullare l'autenticazione dell'utente:
<!--- Esegue il logout dell'utente --->
<cfif isDefined('form.faiLogout')>
<cflogout>
</cfif>
<!--- Il codice all'interno di <cflogin> viene eseguito solo se l'utente non è già autenticato --->.
<cflogin> <cfif isDefined('form.username') AND isDefined('form.password')>........ </cfif></cflogin><cfoutput>
<p>
<form action="post">
Bentornato <b>#GetAuthUser()#</b>!
<input type="submit" value="Logout" name="faiLogout">
</form>
</p>
</cfoutput>
Il file autenticazione.cfm contiene tutta la logica del sistema di autenticazione. Ora modifichiamo i file da proteggere per impedirne la visualizzazione dagli utenti 1) non autenticati o che 2) non hanno il ruolo giusto.
Per risolvere il primo problema basterà includere il file autenticazione.cfm all'inizio delle pagine. All'interno di esso viene già eseguita la verifica dell'autenticazione ed eventualmente l'inclusione del modulo di login.
9. Inserisci all'inizio dei file elimina.cfm, aggiornamento.cfm e inserimento.cfm il tag:
<cfinclude template="autenticazione.cfm">
E' ora di fare delle prove. Accedi ai file di gestione del sito e verifica il funzionamento del sistema di autenticazione. Come potrai vedere, una volta autenticato avrai accesso a tutte le pagine che richiedono l'autenticazione. Questo perché ColdFusion memorizza le informazioni nelle variabili di Sessione che sono condivise in tutta l'applicazione.
Scriviamo ora la parte relativa alla verifica dei ruoli degli utenti. Abbiamo detto, ad esempio, che solo gli utenti con ruolo "inserimento" possono utilizzare il file "inserimento.cfm". Grazie al tag <cfloginuser>, abbiamo associato alle sessioni degli utenti autenticati il ruolo proveniente dal database. L'attributo "roles" di questo tag consente di assegnare più ruoli separandoli da una virgola: attenzione che gli spazi tra una virgola e l'altra vengono interpretati da ColdFusion, quindi è necessario che la lista dei ruoli (contenuta nei record della tabella LOGIN) non contenga spazi.
10. Apri il file inserimento.cfm e controlla se l'utente è nel ruolo "inserimento":
<!--- inserimento.cfm: inserisce un nuovo record nel database --->.
<cfinclude template="autenticazione.cfm">
<cfif NOT isUserInRole("inserimento")>
<p>Non puoi inserire nuovi album.</p>
<cfinclude template="OnRequestEnd.cfm">
<cfabort>
</cfif>.......
Con la funzione isUserInRole() possiamo verificare se l'utente autenticato appartiene ad un certo ruolo. Nel codice abbiamo quindi inserito un <cfif> che, nel caso in cui il ruolo dell'utente non sia "inserimento", visualizzi il messaggio "Non puoi inserire nuovi album". In questo caso tutto ciò che segue nella pagina inserimento.cfm non deve essere eseguito da ColdFusion. Il tag <cfabort> interrompe quindi l'elaborazione della pagina. Come prima, lo abbiamo preceduto dall'inclusione del file OnRequestEnd.cfm per visualizzare il piede della pagina.
Prova a visitare la pagina "http://localhost:8500/discoteca/inserimento.cfm" con l'utente "alessandro" (password "macromedia"), che non possiede il ruolo "inserimento".
11. Analogamente, modifica il file elimina.cfm verificando che l'utente abbia il ruolo "eliminazione":
<!--- elimina.cfm: permette di eliminare gli album --->.
<cfinclude template="autenticazione.cfm">
<cfif NOT isUserInRole("eliminazione")>
<p>Non puoi eliminare gli album.</p>
<cfinclude template="OnRequestEnd.cfm">
<cfabort>
</cfif>........
12. Idem per il file aggiornamento.cfm:
<!--- aggiornamento.cfm: permette di aggiornare gli album --->.
<cfinclude template="autenticazione.cfm">
<cfif NOT isUserInRole("modifica")>
<p>Non puoi modificare gli album.</p>
<cfinclude template="OnRequestEnd.cfm">
<cfabort>
</cfif>........
Abbiamo creato così un buon sistema di autenticazione, facilmente riciclabile all'interno delle pagine del sito. Puoi estendere questa funzionalità creando un sistema di registrazione online che aggiorni la tabella LOGIN inserendo ad esempio nuovi utenti affidando loro un ruolo predefinito.