Dove dovrebbero essere effettuati i controlli delle autorizzazioni utente e MVC e da chi?


26

I controlli delle autorizzazioni degli utenti devono essere effettuati nel modello o nel controller? E chi dovrebbe gestire i controlli delle autorizzazioni, l'oggetto Utente o qualche aiuto UserManagement?

Dove dovrebbe succedere?

Verifica nel controller:

class MyController {
  void performSomeAction() {
    if (user.hasRightPermissions()) {
      model.someAction();
    }
  }
  ...

Avere i controlli nel Controller aiuta a rendere semplici i Modelli, in modo da poter mantenere tutta la logica con i Controller.

Verifica nel modello:

class MyModel {
  void someAction() {
    if (user.hasRightPermissions()) {
      ...
    }
  }
  ...

Inserendo i controlli nel Modello, compliciamo il Modello, ma ci assicuriamo anche che non consentiamo accidentalmente agli utenti di fare cose che non sono tenuti a fare nel Controller.

E da chi?

Una volta stabilito il posto, chi dovrebbe fare i controlli? L'utente?

Class User {
  bool hasPermissions(int permissionMask) {
    ...
  }
  ...

Ma non è davvero responsabilità dell'utente sapere cosa può accedere, quindi forse una classe di supporto?

Class UserManagement {
  bool hasPermissions(User user, int permissionMask) {
    ...
  }
  ...

So che è comune porre una sola domanda, beh, una domanda, ma penso che si possa rispondere bene a queste domande insieme.

Risposte:


20

Come al solito, "dipende"

  • i controlli delle autorizzazioni funzioneranno funzionalmente ovunque sia conveniente metterli,
  • ma se stai ponendo una domanda tecnica, la risposta potrebbe essere "metti i controlli nell'oggetto che possiede i dati richiesti per eseguire il controllo" (che è probabilmente il controller).
  • ma se stai ponendo una domanda filosofica, ti suggerisco una risposta alternativa: non mostrare agli utenti azioni che non possono eseguire .

Quindi, in quest'ultimo caso, potresti avere il controllo delle autorizzazioni nel controller implementato come proprietà booleana e associare quella proprietà alla proprietà Visible del pulsante o del pannello nell'interfaccia utente che controlla l'azione

come utente, è frustrante vedere pulsanti per azioni che non posso eseguire; mi sembra di essere lasciato fuori dal divertimento;)


La nostra applicazione implementa il terzo scenario con l'eccezione che non nascondiamo i controlli, li disabilitiamo. Sfortunatamente è tutto fatto nel codice sottostante di Winforms, quindi non è veramente rilevante per la domanda OP.
Dave Nay,

11
"è frustrante vedere pulsanti per azioni che non posso eseguire" -> Prova a votare il tuo post :)
Rowan Freeman,

5
Non è sufficiente nascondere semplicemente i pulsanti per le azioni che l'utente non può eseguire, il server deve controllare ogni richiesta di autorizzazione. Il terzo punto elenco non è "una risposta alternativa", è qualcosa da fare oltre a verificare le autorizzazioni sul lato server.
Flimm,

@Flimm concordato, se le richieste sono gestite da un server; la domanda specifica riguardava la classe Controller
Steven A. Lowe,

7

La sicurezza è una preoccupazione trasversale, pertanto deve essere implementata su più livelli. Quello che segue è un esempio per MVC ma il concetto si applica ad altre architetture e / o modelli, devi solo identificare i punti di applicazione.

Dove dovrebbe succedere?

Le viste potrebbero contenere elementi dell'interfaccia utente (widget, pulsanti, menu ecc.) Che devono essere visualizzati o meno per alcuni utenti, in base alle loro autorizzazioni. Questa potrebbe essere una responsabilità del motore di visualizzazione , dal momento che non si desidera che ogni vista gestisca questo da solo. A seconda del tipo di elementi che stai autorizzando a freddo, sposta questa responsabilità in un altro posto. Ad esempio, pensa a un menu in cui alcuni elementi devono essere visualizzati e altri no. Gli elementi possono essere implementati come un elenco da qualche parte e filtrare tale elenco in base alle autorizzazioni, quindi inoltrarlo alla vista.

I controller rispondono alle richieste, quindi se un utente non dispone dell'autorizzazione per eseguire un'azione, è necessario verificarlo prima che l'azione venga invocata, spostando la responsabilità sul creatore dell'azione anziché tenerla nel controller. Ciò ha il vantaggio di mantenere pulito il controller e se qualcosa cambia nelle autorizzazioni non è necessario setacciare i controller per applicare tali modifiche.

Le risorse vengono visualizzate in base alle autorizzazioni. Ciò avviene normalmente a livello di database , poiché non si desidera estrarre tutto dal database e quindi applicare le autorizzazioni.

Come puoi vedere, a seconda di ciò che vuoi autorizzare ci sono diversi posti in cui ciò dovrebbe essere fatto. L'obiettivo è quello di essere il più discreto possibile, in modo che quando cambiano i criteri di sicurezza sia possibile applicarli facilmente, preferibilmente senza alterare il codice dell'applicazione. Questo potrebbe non essere valido per piccole applicazioni, in cui il set di autorizzazioni è piuttosto piccolo e non cambia molto spesso. Nelle applicazioni aziendali, tuttavia, la storia è piuttosto diversa.

Chi dovrebbe farlo?

Chiaramente non il modello. Ogni livello dovrebbe avere un punto di applicazione che gestisca l'autorizzazione. Il testo in corsivo sopra evidenzia il possibile punto di applicazione per ogni livello.

Dai un'occhiata a XACML . Non è necessario implementarlo così com'è, ma ti darà alcune indicazioni che potresti seguire.


Questa è la risposta migliore Per qualche ragione, il primo e gli altri affrontano le differenze tra controller e vista, o vista e modello, che non è ciò che l'OP sta chiedendo. Grazie!
redFur

1

Uso il seguente schema. Vale la pena dire che la maggior parte dei controlli delle autorizzazioni degli utenti può essere suddivisa in due casi generali:

  • l'accesso dell'utente all'azione del controller in base al ruolo utente senza verificare i parametri con cui viene chiamata l'azione,
  • accesso dell'utente al modello basato su qualsiasi logica o relazione tra un determinato utente e un determinato modello.

L'accesso all'azione del controller senza il controllo degli attributi viene generalmente implementato nei framework MVC. Questo è semplice: definisci le regole, i tuoi utenti hanno un ruolo. È sufficiente verificare che l'utente disponga dell'autorizzazione all'azione per cercare il suo ruolo nelle regole.

L'accesso dell'utente a un modello particolare deve essere definito nel modello. (L'attore è la classe utente di base. Supponiamo che possa essere cliente, venditore o ospite.)

interface ICheckAccess
{
    public function checkAccess(Actor $actor, $role);
}

class SomeModel implements ICheckAccess
{
    public function checkAccess(Actor $actor, $role)
    {
        // Your permissions logic can be as sophisticated as you want.
    }
}

Inserire questa logica nel modello porta un certo profitto. Il metodo di controllo dell'accesso può essere ereditato, non è necessario creare alcuna classe aggiuntiva, è possibile utilizzare i vantaggi OOP generali.

Successivamente, per semplificare il controllo degli accessi, prendiamo alcune ipotesi che sono quasi sempre implementate già per semplicità e buon stile:

  • di solito i controller sono correlati ad alcune classi di modelli;
  • le azioni a cui è stato verificato l'accesso accettano come parametro un singolo ID modello;
  • questo parametro è sempre accessibile in modo uniforme dal metodo della classe controller di base;
  • l'azione viene posizionata nel controller corrispondente al modello che l'azione ID esegue.

Con questi presupposti, le azioni che utilizzano l'ID modello possono essere associate a un'istanza del modello particolare. In effetti, la maggior parte delle azioni può essere facilmente trasformata e spostata per adattarsi alle ipotesi di cui sopra.

Quindi, è necessario definire ed ereditare alcune classi di controller astratte di base.

abstract class ModelController
{
    // Retrieve model from database using id from action parameter.
    public abstract function loadModel($id);

    // Returns rules for user role to pass to SomeModel::checkAccess()
    // Something like array('view' => 'viewer', 'delete' => 'owner', 'update' => 'owner')
    public abstract function modelRules();

    public abstract fucntion getIdParameter();

    public function filterModelAccess()
    {
        $id = $this->getIdParameter();
        if(!$this->checkModelAccess($id))
            throw new HttpException(403);
    }

    public function checkModelAccess($id)
    {
        $model = $this->loadModel($id);
        $actor = My::app()->getActor();
        $rules = $this->modelRules();
        $role = $rules[My::app()->getActionName()];
        return $model->chechAccess($actor, $role);
    }
}

Puoi chiamare il metodo SomeController :: checkModelAccess ($ id) quando costruisci i tuoi menu e decidi se mostrare qualche link.


Mi dispiace per PHP.
George Sovetov,

1

Sia nel modello che nella vista

Nella vista - perché l'interfaccia utente non dovrebbe mostrare gli elementi dell'interfaccia utente che sono limitati all'utente corrente

(come, diciamo, il pulsante "Elimina" dovrebbe essere mostrato alle persone con le autorizzazioni appropriate)

Nel modello , perché la tua app probabilmente ha un qualche tipo di API, giusto? L'API deve controllare anche le autorizzazioni e probabilmente riutilizza il modello.

(ad esempio, se hai il pulsante "Elimina" nell'interfaccia utente e il metodo API "http: / server / API / DeleteEntry / 123" contemporaneamente


Perché hai scelto il modello sul controller?
Flimm,

non so perché visualizzare, modellare e non nel controller, dove il più delle volte è fatto.
VP.

@VP il controller non ha il potere di mostrare / nascondere gli elementi dell'interfaccia utente (oltre a passare un bool-var alla vista)
jitbit

Non lo so, di solito viene fatto ovunque nel livello del controller, ecco perché ero curioso.
VP.

0

MVC è un modello di presentazione. In quanto tale visione e controllore dovrebbero avere solo responsabilità riguardo alla presentazione. Alcune autorizzazioni si applicano alla presentazione, come una modalità esperto, funzionalità dell'interfaccia utente sperimentale o design diversi. Questi possono essere gestiti dal controller MVC.

Molti altri tipi di autorizzazioni sono rilevanti su diversi livelli dell'applicazione. Ad esempio, se si desidera avere utenti che possono solo visualizzare i dati e non modificare le cose:

  • il livello di presentazione deve nascondere le funzioni di modifica
  • Se viene comunque chiamata una funzione di modifica, questa potrebbe / dovrebbe essere rilevata (dalle parti specifiche dell'applicazione del livello aziendale, non dalla parte specifica del dominio - TrainEditor, non Train) e probabilmente causerà un'eccezione
  • Anche il livello di accesso ai dati può verificare la presenza di scritture, ma per tipi di autorizzazioni più complessi che richiedono rapidamente troppa conoscenza del livello aziendale per essere una buona idea.

C'è qualche duplicazione in questo approccio. Tuttavia, poiché la presentazione è generalmente volatile, si può fare un buon caso per verificare l'autorizzazione nella parte solitamente più stabile dell'applicazione, anche se ciò significa che alcuni controlli ridondanti nel caso in cui il livello di presentazione funzioni come previsto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.