risposta

115

ho creato un nuovo progetto che contiene un provider di appartenenze personalizzato e sovrascritto il metodo ValidateUser dal MembershipProvider classe astratta:

public class MyMembershipProvider : MembershipProvider 
{ 
    public override bool ValidateUser(string username, string password) 
    {  
     // this is where you should validate your user credentials against your database. 
     // I've made an extra class so i can send more parameters 
     // (in this case it's the CurrentTerritoryID parameter which I used as 
     // one of the MyMembershipProvider class properties). 

     var oUserProvider = new MyUserProvider(); 
     return oUserProvider.ValidateUser(username,password,CurrentTerritoryID); 
    } 
} 

Poi ho collegato che provider per il mio progetto ASP.NET MVC 2 con l'aggiunta di un riferimento e indicando fuori dal mio web.config:

<membership defaultProvider="MyMembershipProvider"> 
    <providers> 
     <clear /> 
     <add name="MyMembershipProvider" 
      applicationName="MyApp" 
      Description="My Membership Provider" 
      passwordFormat="Clear" 
      connectionStringName="MyMembershipConnection" 
      type="MyApp.MyMembershipProvider" /> 
    </providers> 
</membership> 

ho bisogno per creare una classe personalizzata che eredita la classe astratta RoleProvider e sostituisce il GetRolesForUser incontrato hod. L'autorizzazione MVC ASP.NET utilizza tale metodo per individuare i ruoli assegnati all'utente corrente connesso e assicurarsi che all'utente sia consentito l'accesso all'azione del controllore.

Questi sono i passi che dobbiamo prendere:

1) Creare una classe personalizzata che eredita la classe astratta RoleProvider e l'override del metodo GetRolesForUser:

public override string[] GetRolesForUser(string username) 
{ 
    SpHelper db = new SpHelper(); 
    DataTable roleNames = null; 
    try 
    { 
     // get roles for this user from DB... 

     roleNames = db.ExecuteDataset(ConnectionManager.ConStr, 
        "sp_GetUserRoles", 
        new MySqlParameter("_userName", username)).Tables[0]; 
    } 
    catch (Exception ex) 
    { 
     throw ex; 
    } 
    string[] roles = new string[roleNames.Rows.Count]; 
    int counter = 0; 
    foreach (DataRow row in roleNames.Rows) 
    { 
     roles[counter] = row["Role_Name"].ToString(); 
     counter++; 
    } 
    return roles; 
} 

2) Collegare il provider di ruoli con la ASP.NET MVC 2 applicazione tramite il nostro web.config:

<system.web> 
... 

<roleManager enabled="true" defaultProvider="MyRoleProvider"> 
    <providers> 
     <clear /> 
     <add name="MyRoleProvider" 
      applicationName="MyApp" 
      type="MyApp.MyRoleProvider" 
      connectionStringName="MyMembershipConnection" /> 
    </providers> 
</roleManager> 

... 
</system.web> 

3) Impostare l'Autorizza (ruoli = "xxx, yyy") sopra la voluta controller/Azione:

[Authorization(Roles = "Customer Manager,Content Editor")] 
public class MyController : Controller 
{ 
    ...... 
} 

Questo è tutto! Ora funziona!

4) Opzionale: impostare un Authorize attributo personalizzato in modo che possiamo reindirizzare un ruolo indesiderato a un AccessDenied Pagina:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
public class MyAuthorizationAttribute : AuthorizeAttribute 
{ 
    /// <summary> 
    /// The name of the master page or view to use when rendering the view on authorization failure. Default 
    /// is null, indicating to use the master page of the specified view. 
    /// </summary> 
    public virtual string MasterName { get; set; } 

    /// <summary> 
    /// The name of the view to render on authorization failure. Default is "Error". 
    /// </summary> 
    public virtual string ViewName { get; set; } 

    public MyAuthorizationAttribute() 
     : base() 
    { 
     this.ViewName = "Error"; 
    } 

    protected void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus) 
    { 
     validationStatus = OnCacheAuthorization(new HttpContextWrapper(context)); 
    } 

    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) 
     { 
      throw new ArgumentNullException("filterContext"); 
     } 

     if (AuthorizeCore(filterContext.HttpContext)) 
     { 
      SetCachePolicy(filterContext); 
     } 
     else if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      // auth failed, redirect to login page 
      filterContext.Result = new HttpUnauthorizedResult(); 
     } 
     else if (filterContext.HttpContext.User.IsInRole("SuperUser")) 
     { 
      // is authenticated and is in the SuperUser role 
      SetCachePolicy(filterContext); 
     } 
     else 
     { 
      ViewDataDictionary viewData = new ViewDataDictionary(); 
      viewData.Add("Message", "You do not have sufficient privileges for this operation."); 
      filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData }; 
     } 
    } 

    protected void SetCachePolicy(AuthorizationContext filterContext) 
    { 
     // ** IMPORTANT ** 
     // Since we're performing authorization at the action level, the authorization code runs 
     // after the output caching module. In the worst case this could allow an authorized user 
     // to cause the page to be cached, then an unauthorized user would later be served the 
     // cached page. We work around this by telling proxies not to cache the sensitive page, 
     // then we hook our custom authorization code into the caching mechanism so that we have 
     // the final say on whether a page should be served from the cache. 
     HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache; 
     cachePolicy.SetProxyMaxAge(new TimeSpan(0)); 
     cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */); 
    } 
} 

Ora possiamo usare la nostra attributo fatta per reindirizzare i nostri utenti di accedere alla visualizzazione negata:

[MyAuthorization(Roles = "Portal Manager,Content Editor", ViewName = "AccessDenied")] 
public class DropboxController : Controller 
{ 
    ....... 
} 

Questo è tutto! Super duper!

Ecco alcuni dei link che ho usato per ottenere tutte queste informazioni:

personalizzato provider di ruoli: http://davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx

Spero che queste informazioni aiuta!

+0

il modo in cui hai spiegato questo è sensazionale !! e scommetto che non ci stavi nemmeno provando così tanto ... dovresti considerare di scrivere post sul blog :). –

+0

il modo in cui hai spiegato questo è sensazionale !! e scommetto che non ci stavi nemmeno provando così tanto ... dovresti considerare di scrivere post sul blog :). –

+2

grazie amico, felice che abbia aiutato. Mi trovo a farlo spesso e, facendolo, lo capisco meglio :-) – danfromisrael

1

ho usato il codice sorgente del NauckIt.PostgreSQL del provider come base, e modificato per soddisfare le mie esigenze.

8

E 'anche possibile utilizzare questo con una quantità di codice molto più piccola, non sono del tutto sicuro se questo metodo è sicuro ma funziona molto bene con qualsiasi database che si utilizza.

nel globale.ASax

protected void Application_AuthenticateRequest(object sender, EventArgs e) 
    { 
     if (HttpContext.Current.User != null) 
     { 
      if (HttpContext.Current.User.Identity.IsAuthenticated) 
      { 
       if (HttpContext.Current.User.Identity is FormsIdentity) 
       { 
        FormsIdentity id = 
         (FormsIdentity)HttpContext.Current.User.Identity; 
        FormsAuthenticationTicket ticket = id.Ticket; 

        // Get the stored user-data, in this case, our roles 
        string userData = ticket.UserData; 
        string[] roles = userData.Split(','); 
        HttpContext.Current.User = new GenericPrincipal(id, roles); 
       } 
      } 
     } 
    } 

ciò che questo non è che legge i ruoli del authCookie che è stato fatto da FormsAuthenticationTicket

e la logica di accesso assomiglia a questo

public class dbService 
{ 
    private databaseDataContext db = new databaseDataContext(); 

    public IQueryable<vwPostsInfo> AllPostsAndDetails() 
    { 
     return db.vwPostsInfos; 
    } 

    public IQueryable<role> GetUserRoles(int userID) 
    { 
     return (from r in db.roles 
        join ur in db.UsersRoles on r.rolesID equals ur.rolesID 
        where ur.userID == userID 
        select r); 
    } 

    public IEnumerable<user> GetUserId(string userName) 
    { 
     return db.users.Where(u => u.username.ToLower() == userName.ToLower()); 
    } 

    public bool logOn(string username, string password) 
    { 
     try 
     { 
      var userID = GetUserId(username); 
      var rolesIQueryable = GetUserRoles(Convert.ToInt32(userID.Select(x => x.userID).Single())); 
      string roles = ""; 
      foreach (var role in rolesIQueryable) 
      { 
       roles += role.rolesName + ","; 
      } 

      roles.Substring(0, roles.Length - 2); 
      FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
         1, // Ticket version 
         username, // Username associated with ticket 
         DateTime.Now, // Date/time issued 
         DateTime.Now.AddMinutes(30), // Date/time to expire 
         true, // "true" for a persistent user cookie 
         roles, // User-data, in this case the roles 
         FormsAuthentication.FormsCookiePath);// Path cookie valid for 

      // Encrypt the cookie using the machine key for secure transport 
      string hash = FormsAuthentication.Encrypt(ticket); 
      HttpCookie cookie = new HttpCookie(
       FormsAuthentication.FormsCookieName, // Name of auth cookie 
       hash); // Hashed ticket 

      // Set the cookie's expiration time to the tickets expiration time 
      if (ticket.IsPersistent) cookie.Expires = ticket.Expiration; 

      // Add the cookie to the list for outgoing response 
      HttpContext.Current.Response.Cookies.Add(cookie); 

      return true; 
     } 
     catch 
     { 
      return (false); 
     } 
    } 
} 

devo conservare i ruoli nel mio database con due tabelle: tabella: Ruolo con le colonne: roleID e roleName e la tabella: UsersRoles con le colonne: userID e roleID, questo rende possibile più ruoli per più utenti ed è facile aggiungere la propria logica/rimuovere i ruoli dagli utenti e così via. Ciò consente di utilizzare [Autorizza (Ruoli = "Super amministratore")] ad esempio. spero che questo ti aiuti.

edit: dimenticato di fare la verifica della password, ma si aggiunge solo un caso nel metodo di accesso che controlla se il nome utente e la password forniti controlli su e se non restituisce false

+0

Wait, quindi stai memorizzando i nomi dei ruoli nel cookie auth? Questo non significa che l'utente può inserire qualsiasi ruolo che desidera nel suo auth cookie? Immagino che non importi perché dovrebbero decifrare il cookie? – Pandincus

+0

@Pandincus: Sì, questo è uno dei lati negativi dell'utilizzo di questo metodo se l'utente riesce a decrittografare il cookie, ciò che si potrebbe fare è crittografare ulteriormente i ruoli e fornire una chiave pubblica insieme al cookie per la successiva decodifica nel globale asax. Non è perfetto ma ottiene il lavoro e non è così complesso. – Joakim

Problemi correlati