2016-04-06 27 views
15

Sto cercando di implementare il controllo degli accessi basato su autorizzazione con il nucleo di aspnet. Per la gestione dinamica di ruoli utente e permessi (create_product, delete_product, ecc.), Vengono memorizzati nel database. Data Model è come http://i.stack.imgur.com/CHMPE.pngCome implementare il controllo degli accessi basato su autorizzazione con Asp.Net Core

Prima nucleo ASPNET (in MVC 5) stavo usando personalizzato AuthorizeAttribute come qui di seguito per gestire il problema:

public class CustomAuthorizeAttribute : AuthorizeAttribute 
{ 
    private readonly string _permissionName { get; set; } 
    [Inject] 
    public IAccessControlService _accessControlService { get; set; } 

    public CustomAuthorizeAttribute(string permissionName = "") 
    { 
     _permissionName = permissionName; 
    } 

    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 
     var user = _accessControlService.GetUser(); 
     if (PermissionName != "" && !user.HasPermission(_permissionName)) 
     { 
      // set error result 
      filterContext.HttpContext.Response.StatusCode = 403; 
      return; 
     } 
     filterContext.HttpContext.Items["CUSTOM_USER"] = user; 
    } 
} 

Poi stavo usando in metodo di azione, come di seguito:

[HttpGet] 
[CustomAuthorize(PermissionEnum.PERSON_LIST)] 
public ActionResult Index(PersonListQuery query){ } 

Inoltre, stavo usando HttpContext.Items [ "CUSTOM_USER"] in vista per mostrare o nascondere html parte:

@if (CurrentUser.HasPermission("<Permission Name>")) 
{ 

} 

Quando ho deciso di cambiare il nucleo di aspnet, tutto il mio piano era fallito. Perché non esisteva il metodo virtuale OnAuthorization nello AuthorizeAttribute. Ho provato alcuni modi per risolvere il problema. Quelli sono qui di seguito:

  • Usando nuova autorizzazione basato su policy (penso che non è adatto per il mio scenerio)

  • Utilizzando personalizzati AuthorizeAttribute e AuthorizationFilter (ho letto questo messaggio https://stackoverflow.com/a/35863514/5426333 ma non ho potuto cambiare in modo corretto)

  • Utilizzando middleware personalizzati (come ottenere AuthorizeAttribute di corrente azione?)

  • 0.123.
  • Utilizzando ActionFilter (è corretto per motivi di sicurezza?)

Non riuscivo a decidere quale sia la strada migliore per il mio scenerio e modalità di attuazione.

Prima domanda: l'implementazione di MVC5 è una cattiva pratica?

Seconda domanda: Qualche suggerimento per implementare il nucleo di aspnet?

+0

Perché pensi che l'autorizzazione basata su criteri non sia adatta al tuo caso? Puoi ancora creare 'PermissionRequirement' implementando' IAuthorizationRequirement' e un gestore, quindi aggiungilo come 'options.AddPolicy (" PersonList ", policy => policy.Requirements.Add (new PermissionRequirement (" PersonList ")))' – Tseng

+0

perché , voglio ottenere permessi utente dal database. –

+0

Niente ti impedisce di recuperarli all'interno del gestore. Basta iniettare il tuo contesto/repository/servizio/tutto ciò di cui hai bisogno nel tuo gestore dei requisiti: http://docs.asp.net/en/latest/security/authorization/dependencyinjection.html – Tseng

risposta

28

Sulla base dei commenti, qui un esempio su come utilizzare l'autorizzazione basato su policy:

public class PermissionRequirement : IAuthorizationRequirement 
{ 
    public PermissionRequirement(PermissionEnum permission) 
    { 
     Permission = permission; 
    } 

    public PermissionEnum Permission { get; } 
} 

public class PermissionHandler : AuthorizationHandler<PermissionRequirement> 
{ 
    private readonly IUserPermissionsRepository permissionRepository; 

    public PermissionHandler(IUserPermissionsRepository permissionRepository) 
    { 
     if(permissionRepository == null) 
      throw new ArgumentNullException(nameof(permissionRepository)); 

     this.permissionRepository = permissionRepository; 
    } 

    protected override void Handle(AuthorizationContext context, PermissionRequirement requirement) 
    { 
     if(context.User == null) 
     { 
      // no user authorizedd. Alternatively call context.Fail() to ensure a failure 
      // as another handler for this requirement may succeed 
      return null; 
     } 

     bool hasPermission = permissionRepository.CheckPermissionForUser(context.User, requirement.Permission); 
     if (hasPermission) 
     { 
      context.Succeed(requirement); 
     } 
    } 
} 

e registrarlo nel vostro Startup classe:

services.AddAuthorization(options => 
{ 
    UserDbContext context = ...; 
    foreach(var permission in context.Permissions) 
    { 
     // assuming .Permission is enum 
     options.AddPolicy(permission.Permission.ToString(), 
      policy => policy.Requirements.Add(new PermissionRequirement(permission.Permission))); 
    } 
}); 

// Register it as scope, because it uses Repository that probably uses dbcontext 
services.AddScope<IAuthorizationHandler, PermissionHandler>(); 

E infine nel controller

[HttpGet] 
[Authorize(Policy = PermissionEnum.PERSON_LIST.ToString())] 
public ActionResult Index(PersonListQuery query) 
{ 
    ... 
} 

Il vantaggio di questa soluzione è che si può anche avere multipl Gestori per un requisito, ad esempio se il primo tentativo riesce a determinare il fallimento, è possibile utilizzarlo con resource based authorization con un piccolo sforzo in più.

L'approccio basato su criteri è il metodo preferito per farlo dal team di base di ASP.NET.

Da blowdart:

Noi non vogliamo che tu a scrivere l'autorizzazione attributi personalizzati. Se hai bisogno di farlo, abbiamo fatto qualcosa di sbagliato. Invece dovresti scrivere i requisiti di autorizzazione.

+3

Uso di asp.net core rc1 Non ho potuto utilizzare [Autorizza (Policy = PermissionEnum.PERSON_LIST.ToString())] perché il compilatore richiede di utilizzare solo "costanti" nella dichiarazione di attributo. Il turn-arround doveva usare l'attributo come questo: [Autorizza (Policy = nameof (PermissionEnum.PERSON_LIST))] –

+0

Come modificheresti questa soluzione per far fronte alle regole basate sulle risorse? Ad esempio, ho bisogno di verificare che l'utente abbia il permesso basato su un valore enum E alcune informazioni disponibili solo quando un'azione del controllore è in esecuzione? È quello dove IAuthorizationService deve essere iniettato? – Sam

+0

@Sam Non vorresti semplicemente eseguire i tuoi controlli aggiuntivi nel metodo 'Handle'? –

1

Per una soluzione che non richiede l'aggiunta di una politica per ciascuna autorizzazione, vedere il mio answer per un'altra domanda.

Consente di decorare i controller e le azioni con qualsiasi attributo personalizzato desiderato e accedervi da AuthorizationHandler.

+0

Mi piace molto questa soluzione. È molto pulito e flessibile –