回答

115

我已经创建了一个包含自定义的成员提供一个新的项目,并从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); 
    } 
} 

然后我连接的是提供给我的ASP.NET MVC 2项目通过添加引用并指出它从我的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> 

我需要创建一个继承RoleProvider抽象类和覆盖会见了GetRolesForUser自定义类HOD。 ASP.NET MVC授权使用该方法找出哪些角色分配给当前登录用户,并确保允许用户访问控制器操作。

下面是步骤,我们需要采取:

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)将随角色提供ASP.NET MVC通过我们的web.config 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)设置授权(角色= “XXX,YYY”):

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

就是这样!现在它工作了!

4)可选:设置自定义Authorize属性,这样就可以重定向不需要的角色的存取遭拒页:

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

现在我们可以用我们自己的制造属性为我们的用户重定向到访问拒绝观点:

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

就是这样! 超级骗子!

下面是一些我使用来获取所有这些信息的链接:

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

我希望这个信息帮助!

+0

你已经解释这是令人震惊的方式!我敢打赌,你甚至都没有努力尝试......你应该考虑写博客文章:)。 – 2010-05-06 13:44:55

+0

你已经解释过这是轰动的方式!我敢打赌,你甚至都没有努力尝试......你应该考虑写博客文章:)。感谢伙计 – 2010-05-06 13:46:22

+2

,很高兴它有帮助。我发现自己经常这样做,通过做我自己更好地理解它:-) – danfromisrael 2010-05-06 19:42:07

1

我用NauckIt.PostgreSQL提供商的源代码为基础,并修改它适合我的需要。

8

它也可以使用这个少得多的代码量,我不完全确定,如果这种方法是安全的,但对任何你使用的数据库工作得很好。

在全球范围内。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); 
       } 
      } 
     } 
    } 

这是什么一样的是,它读取来自authCookie这是从的FormsAuthenticationTicket

制成,登录逻辑是这样的

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

我的角色存储在我的数据库中的角色有两个表:表:具有列:roleID和roleName以及表的角色:UsersRoles具有列:userID和roleID,这使得多个用户可以有多个角色,并且可以很容易地创建自己的逻辑来添加/从用户中删除角色等等。这使您可以使用[授权(角色=“超级管理员”)]例如。希望这可以帮助。

编辑:忘了做密码检查,但你只是如果在检查登录方法添加的,如果用户名和密码提供了检查,如果没有返回false

+0

等待,因此您将角色名称存储在auth cookie中?这是不是意味着用户可以将他们想要的任何角色放入auth cookie中?我想这不重要,因为他们必须解密cookie。 – Pandincus 2011-02-26 21:02:17

+0

@Pandincus:是的,如果用户设法解密cookie,那么这就是使用这种方法的缺点之一,那么可以做的是进一步加密角色并提供一个公共密钥和cookie一起用于以后在全球解密的.asax。这并不完美,但它完成了工作,并没有那么复杂。 – Joakim 2011-02-28 09:57:17

相关问题