wake-up-neo.com

Wie erstelle ich einen benutzerdefinierten Mitgliedschaftsanbieter für ASP.NET MVC 2?

Wie erstelle ich eine benutzerdefinierte Mitgliedschaft für ASP.NET MVC 2 basierend auf dem ASP.NET-Mitgliedschaftsanbieter?

58
ilija veselica

Ich habe ein neues Projekt erstellt, das einen benutzerdefinierten Mitgliedschaftsanbieter enthält, und die Methode ValidateUser aus der abstrakten Klasse MembershipProvider überschrieben:

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);
    }
}

Dann habe ich diesen Anbieter mit meinem ASP.NET MVC 2-Projekt verbunden, indem ich einen Verweis hinzugefügt und in meiner web.config darauf hingewiesen habe:

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

Ich muss eine benutzerdefinierte Klasse erstellen, die die abstrakte Klasse RoleProvider erbt und die Methode GetRolesForUser überschreibt. Die ASP.NET MVC-Autorisierung ermittelt anhand dieser Methode, welche Rollen dem aktuell angemeldeten Benutzer zugewiesen sind, und stellt sicher, dass der Benutzer auf die Controller-Aktion zugreifen darf.

Hier sind die Schritte, die wir unternehmen müssen:

1) Erstellen Sie eine benutzerdefinierte Klasse, die die abstrakte Klasse RoleProvider erbt und die Methode GetRolesForUser überschreibt:

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) Verbinden Sie den Rollenanbieter über unsere web.config mit der ASP.NET MVC 2-Anwendung:

<system.web>
...

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

...
</system.web>

3) Setzen Sie die Berechtigung (Rollen = "xxx, yyy") über den gewünschten Controller/Aktion:

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

Das ist es! Jetzt funktioniert es!

4) Optional: Legen Sie ein benutzerdefiniertes Authorize -Attribut fest, damit wir eine unerwünschte Rolle auf eine AccessDenied-Seite umleiten können:

[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 */);
    }
}

Jetzt können wir unser eigenes Attribut verwenden, um unsere Benutzer zum Zugriff auf die verweigerte Ansicht umzuleiten:

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

Das ist es! Super duper!

Hier sind einige der Links, über die ich all diese Informationen abgerufen habe:

Benutzerdefinierter Rollenanbieter: http://davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx

Ich hoffe diese Info hilft!

116
danfromisrael
10
uriDium

Es ist auch möglich, dies mit einer viel geringeren Menge an Code zu verwenden. Ich bin nicht ganz sicher, ob diese Methode so sicher ist, aber sie funktioniert sehr gut mit jeder von Ihnen verwendeten Datenbank.

in der global.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);
                }
            }
        }
    }

das bedeutet, dass die Rollen aus dem AuthCookie gelesen werden, der mit FormsAuthenticationTicket erstellt wurde

und die Anmeldelogik sieht so aus

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);
        }
    }
}

ich speichere die Rollen in meiner Datenbank mit zwei Tabellen: Tabelle: Rolle, die die Spalten hat: Rollen-ID und Rollenname und die Tabelle: BenutzerRollen, die die Spalten hat: Benutzer-ID und Rollen-ID, dies ermöglicht mehrere Rollen für mehrere Benutzer und es ist einfach zu Erstellen Sie Ihre eigene Logik zum Hinzufügen/Entfernen von Rollen zu Benutzern usw. Auf diese Weise können Sie beispielsweise [Authorize (Roles = "Super Admin")] verwenden. hoffe das hilft.

edit: habe vergessen, das Passwort zu überprüfen, aber du fügst einfach ein if in die logOn-Methode ein, das prüft, ob der angegebene Benutzername und das angegebene Passwort geprüft werden und wenn nicht, wird false zurückgegeben

8
Joakim

Ich habe den Quellcode des NauckIt.PostgreSQL-Providers als Basis verwendet und an meine Bedürfnisse angepasst.

1
SztupY