ответ

115

Я создал новый проект, содержащий пользовательский поставщик членства и отменяю метод ValidateUser из MembershipProvider абстрактного класса:

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 встретились корыто. Авторизация 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 2 приложений с помощью нашего 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) Установите Авторизовать (Роли = "ххх, ууу") выше разыскиваемого Controller/Action:

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

Вот и все! Теперь это работает!

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

Теперь мы можем использовать наш собственный сделанный атрибут, чтобы перенаправить наших пользователей доступ запрещен вид:

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

Всё! Супер пупер!

Вот некоторые ссылки, которые я использовал, чтобы получить всю эту информацию:

пользовательского поставщика ролей: http://davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx

Я надеюсь, что эта информация поможет!

+0

как вы объяснили это сенсационно !! и я уверен, вы даже не пытались это сделать ... вы должны рассмотреть возможность написания сообщений в блоге :). –

+0

как вы объяснили это сенсационно !! и я уверен, вы даже не пытались это сделать ... вы должны рассмотреть возможность написания сообщений в блоге :). –

+2

спасибо, приятель, рад, что это помогло. я часто нахожу, что делаю это, и, делая это, я лучше понимаю это :-) – danfromisrael

10
+0

спасибо, я постараюсь это, похоже, это то, что мне нужно. –

+0

Хороший пример :) – bot

+0

Можете ли вы обобщить основные моменты из ссылки, которые отвечают на вопрос пользователя, а также предоставляют ссылку? –

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

я хранить роли в моей базе данных с двумя таблицами: table: Role, в которой есть столбцы: roleID и roleName и таблица: UsersRoles, у которых есть столбцы: userID и roleID, это дает возможность для нескольких ролей для нескольких пользователей, и легко сделать свою собственную логику для добавления/удалить роли от пользователей и так далее. Это позволяет вам использовать [Authorize (Roles = «Super Admin»)]. надеюсь это поможет.

редактировать: забыл сделать проверку пароля, но вы просто добавить, если в методе LogOn, который проверяет, если имя пользователя и пароль, при условии, проверяет и, если не возвращает ложное

+0

Подождите, так что вы сохраняете имена ролей в файле cookie auth? Разве это не означает, что пользователь может помещать любые роли, которые они хотят в свой файл cookie? Думаю, это не имеет значения, потому что им придется расшифровывать файлы cookie? – Pandincus

+0

@Pandincus: Да, это один из недостатков использования этого метода, если пользователю удастся расшифровать файл cookie, что можно сделать, это еще больше зашифровать роли и предоставить открытый ключ вместе с куки-файлом для последующего расшифровки в глобальном .asax. Это не идеально, но он выполняет свою работу и не настолько сложный. – Joakim