Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Filtri e autenticazione con Ruby on Rails

Metodi da eseguire prima o dopo gruppi di action per fattorizzare il codice. Esempio: autenticazione
Metodi da eseguire prima o dopo gruppi di action per fattorizzare il codice. Esempio: autenticazione
Link copiato negli appunti

Secondo il pattern MVC seguito da Rails, il controller ha il compito di rispondere alle richieste del client utilizzando i modelli per interagire con i dati e le viste per interagire con gli stessi client.

Utilizzando i filtri, Rails permette di definire una catena di metodi da eseguire prima o dopo l'esecuzione delle action di un controller; l'utilizzo dei filtri risulta particolarmente utile quando una stessa porzione di codice deve essere eseguita in action differenti.

I filtri sono dichiarati all'interno di un controller e coinvolgono esclusivamente le action di quel controller; se i filtri interessano le action di controller differenti all'interno della stessa applicazione allora sono definiti nel controller ApplicationController.

Esistono tre tipi di filtri:

  • before_filter - definisce il codice eseguire prima di una action
  • around_filter - definisce il codice da eseguire prima e dopo una action
  • after_filter - definisce il codice eseguire dopo una action

L'ordine di esecuzione nel caso in cui tutti e tre i tipi di filtri siano definiti per una stessa action è la seguente:

  1. before_filter
  2. around_filter
  3. action
  4. around_filter
  5. after_filter

Quando si dichiara un filtro è possibile specificare una catena di metodi da eseguire; ad esempio:

before_filter [:metodo_uno, : metodo_due]

oppure un block contenente direttamente il codice:

before_filter do |controller, action|
 ...
end

Per illustrare il funzionamento dei filtri utilizziamo un'applicazione di esempio. Supponiamo di voler gestire delle note riservate, consultabili solo dopo l'autenticazione con nome utente e password differenti per ogni nota. Una volta introdotte le credenziali per una nota, questa viene visualizzata a schermo e quindi subito cancellata dal database.

L'applicazione deve inoltre consentire ad un amministratore, con nome utente e password propri, di creare nuove note e gestire quelle già esistenti.

Creiamo una nuova applicazione e la risorsa Note con tutte le action necessarie per la sua gestione. Utilizziamo lo script scaffold per generare automaticamente routing, modello, pagine e controller.

rails notes
cd notes
ruby script/generate scaffold Note title:string content:text username:string password:string
rake db:migrate

I campi username e password saranno conservati in chiaro nome utente e password da introdurre per la visualizzazione della singola nota. Dal punto di vista della sicurezza questa non è una buona soluzione, ma ci permette di indagare il funzionamento dei filtri senza introdurre altri concetti di disturbo.

before_filter

Puntiamo il browser all'indirizzo http://localhost:3000/notes per controllare che tutte le funzionalità base di gestione della risorsa Note (creazione, modifica, visualizzazione, cancellazione) siano presenti. Per proteggere con autenticazione le action che abbiamo appena creato ricorreremo a authenticate_or_request_with_http_basic del modulo ActionController::HttpAuthentication::Basic per definire una semplice funzione di autenticazione HTTP con username e password fissi per l'amministratore; poiché non abbiamo intenzione di rendere questo nuovo metodo accessibile direttamente al client, aggiungiamo il codice in fondo al controller, preceduto dalla parola chiave private:

file: app/controllers/notes_controller.rb
...
  def destroy
    @note = Note.find(params[:id])
    @note.destroy

    respond_to do |format|
      format.html { redirect_to(notes_url) }
      format.xml  { head :ok }
    end
  end
  
private
  
  def check_authentication
    authenticate_or_request_with_http_basic do |username, password|
      username == "nome" && password == "parola"
    end
  end

end

Il metodo check_authentication esegue un'autenticazione HTTP riconoscendo come validi solo il nome utente e la password specificati nel codice; se le credenziali non sono inserite correttamente restituisce il codice HTTP 401 Unauthorized e blocca l'esecuzione della richiesta. Volendo richiedere l'inserimento delle credenziali di autenticazione per tutte le action del controller definiamo il seguente filtro:

class NotesController < ApplicationController
  
  # filtro con il solo metodo check_authentication da eseguire
  # prima di ogni azione del controller
  before_filter :check_authentication
  
  # GET /notes
  # GET /notes.xml
  def index
...

Avviamo il server dal prompt dei comandi con ruby script/server e osserviamo come accedendo a http://localhost:3000/notes venga richiesta l'immissione delle credenziali di autenticazione. Se si inseriscono credenziali sbagliate, si osserva, dal log stampato a prompt, come il metodo check_authentication abbia bloccato l'esecuzione della richiesta restituendo il codice HTTP 401.

Processing NotesController#index (for 127.0.0.1 at 2008-11-23 19:27:49) [GET]
Filter chain halted as [:check_authentication] rendered_or_redirected.
Completed in 1ms (View: 0, DB: 0) | 401 Unauthorized [http://localhost/notes]

Una volta autenticati è possibile accedere a tutte le action del controller indistintamente, fino a che la sessione del browser non sarà distrutta chiudendo e riaprendo il browser.

Ora occupiamoci dell'action show, che permette di visualizzare una singola nota. Per questa action vogliamo che il client si autentichi inserendo il nome utente e la password relativa alla singola nota salvati nel database; il metodo di autenticazione sarà quindi:

def check_note_authentication
  # estraggo l'istanza di note dal database
  @note = Note.find(params[:id])
  
  # richiedo autenticazione HTTP confrontando nome utente e password
  # con quelli specificati nel database
  authenticate_or_request_with_http_basic do |username, password|
    username == @note.username && password == @note.password
  end
end

Inserito il metodo subito sotto il metodo check_authentication, così che risulti anche questo un metodo privato non accessibile direttamente dal client, raffiniamo la dichiarazione del filtro così da attivare check_authentication per tutte le action tranne che per show, ed attivare check_note_authentication solo per questa action:

# filtro con il solo metodo check_authentication da eseguire
# prima di ogni azione del controller tranne show
before_filter :check_authentication, :except => :show

# filtro con il solo metodo check_note_authentication da attivare
# solo per l'action show
before_filter :check_note_authentication, :only => :show

Consultando la lista delle note (http://localhost:3000/notes), viene richiesta l'autenticazione con le credenziali inserite nel metodo check_note_authentication, quelle dell'amministratore; selezionando il link show corrispondente ad una delle note viene invece richiesta l'immissione delle credenziali definite per quella nota, e il testo viene visualizzato solo se si immettono le credenziali corrette.

after_filter

Una volta visualizzato il testo della nota, vogliamo che questa venga cancellata dal database. Anche se questa operazione potrebbe essere inserita normalmente nel codice della action show, procederemo a definire un metodo da eseguire tramite filtro ogni volta che l'action show è terminata. Analogamente al caso precedente definiamo il metodo privato delete_read_note, posizionandolo sotto il metodo check_note_authentication:

def delete_read_note
  @note.delete
end

Inseriamo nella catena dei metodi da eseguire dopo l'esecuzione della action show il metodo appena definito:

...
  before_filter :check_note_authentication, :only => :show
  after_filter :delete_read_note, :only => :show
...

Consultando una delle note precedentemente introdotte nel database e tornado alla lista delle note notiamo come le note visualizzate non siano più presenti.

È interessante osservare come all'interno del metodo delete_read_note si sia utilizzata la variabile @note, definita all'interno della action show; le variabili sono quindi condivise fra i filtri e le relative action.

around_filter

Definiamo come ultimo un metodo contenente codice da eseguire prima e dopo l'esecuzione di ogni action:

...
around_filter :print_something
...

  def print_something
    puts "Prima della action"
    yield
    puts "Dopo la action"
  end

end

La action viene eseguita in corrispondenza di yield; tutto quello che appare prima e dopo yield viene eseguito rispettivamente prima e dopo la action. In assenza di uno yield all'interno del codice del metodo la action non viene eseguita e il flusso dell'applicazione Rails interrotto.

Ti consigliamo anche