简体   繁体   English

如何为ASP.NET MVC 2创建自定义成员资格提供程序?

[英]How do I create a custom membership provider for ASP.NET MVC 2?

如何基于ASP.NET成员资格提供程序为ASP.NET MVC 2创建自定义成员资格?

I have created a new project containing a custom membership provider and overrode the ValidateUser method from the MembershipProvider abstract class: 我创建了一个包含自定义成员资格提供程序的新项目,并覆盖了MembershipProvider抽象类中的ValidateUser方法:

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

Then I connected that provider to my ASP.NET MVC 2 project by adding a reference and pointing it out from my web.config: 然后我通过添加引用并从web.config指出它将该提供程序连接到我的ASP.NET MVC 2项目:

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

I do need to create a custom class that inherits the RoleProvider abstract class and overrides the GetRolesForUser method. 我确实需要创建一个继承RoleProvider抽象类的自定义类,并重写GetRolesForUser方法。 The ASP.NET MVC Authorizing uses that method to find out which roles are assigned to the current logged-on user and makes sure the user is permitted to access the controller action. ASP.NET MVC授权使用该方法找出分配给当前登录用户的角色,并确保允许用户访问控制器操作。

Here are the steps we need to take: 以下是我们需要采取的步骤:

1) Create a custom class that inherits the RoleProvider abstract class and overrides the GetRolesForUser method: 1)创建一个继承RoleProvider抽象类的自定义类,并重写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) Connect the role provider with the ASP.NET MVC 2 application via our web.config: 2)通过我们的web.config将角色提供程序与ASP.NET MVC 2应用程序连接:

<system.web>
...

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

...
</system.web>

3) Set the Authorize(Roles="xxx,yyy") above the wanted Controller / Action: 3)将Authorize(Roles =“xxx,yyy”)设置在所需的Controller / Action上方:

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

That's it! 而已! Now it works! 现在它有效!

4) Optional: set a custom Authorize attribute so we can redirect an unwanted role to an AccessDenied Page: 4)可选:设置自定义Authorize属性,以便我们可以将不需要的角色重定向到AccessDenied页面:

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

Now we can use our own made attribute to redirect our users to access denied view: 现在我们可以使用自己的make属性来重定向用户访问被拒绝的视图:

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

That's it! 而已! Super duper! 超级骗子!

Here are some of the links I've used to get all this info: 以下是我用来获取所有这些信息的一些链接:

Custom role provider: http://davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx 自定义角色提供者: http//davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx

I hope this info helps! 我希望这些信息有所帮助!

Its also possible to use this with a much smaller amount of code, i'm not entirely sure if this method is as safe but works very well with any database you use. 它也可以用更少量的代码来使用它,我不完全确定这种方法是否安全,但对你使用的任何数据库都能很好地工作。

in the global.asax 在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);
                }
            }
        }
    }

what this does is that it reads the roles from the authCookie which was made from FormsAuthenticationTicket 它的作用是它从authCookie中读取由FormsAuthenticationTicket创建的角色

and the logon logic looks like this 并且登录逻辑看起来像这样

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

i store the roles in my database with two tables: table: Role which has the columns: roleID and roleName and the table: UsersRoles wich has the columns: userID and roleID, this makes it possible for multiple roles for several users and it's easy to make your own logic to add/remove roles from users and so forth. 我使用两个表将角色存储在我的数据库中:table:具有列的角色:roleID和roleName以及表:UsersRoles具有列:userID和roleID,这使得多个用户可以使用多个角色,并且很容易制定自己的逻辑来添加/删除用户的角色等等。 This enables you to use [Authorize(Roles="Super Admin")] for instance. 这使您可以使用[Authorize(Roles =“Super Admin”)]。 hope this helps. 希望这可以帮助。

edit: forgot to make the password check but you just add an if in the logOn method which checks if the username and password provided checks up and if not it returns false 编辑:忘记进行密码检查但是你只需在logOn方法中添加一个if,它检查提供的用户名和密码是否检查,如果没有则返回false

我使用NauckIt.PostgreSQL提供程序的源代码作为基础,并根据我的需要对其进行了修改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM