0

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

Мой поставщик клиентов :

namespace MyDomain 
{ 
    public class CustomRoleProvider : RoleProvider 
    { 
     public override string[] GetRolesForUser(string username) 
     { 
      using (MyContext objContext = new MyContext()) 
      { 
       var objUser = objContext.Users.FirstOrDefault(x => x.Username == username); 
       if (objUser == null) 
       { 
        return null; 
       } 
       else 
       { 
        string[] ret = { objUser.Access_Levels.Name }; 
        return ret; 
       } 
      } 
     } 

     public override bool IsUserInRole(string username, string roleName) 
     { 
      var userRoles = GetRolesForUser(username); 
      return userRoles.Contains(roleName); 
     } 
} 

Мой контроллер:

[Authorize(Roles = "Administrator")] 
public class AdminController : Controller 

И Web.Config:

<system.web> 
    <roleManager defaultProvider="CustomRoleProvider" enabled="true" > 
     <providers> 
     <clear /> 
     <add name="CustomRoleProvider" type="Online_Storage_Portal.CustomRoleProvider" cacheTimeoutInMinutes="30"/> 
     </providers> 
    </roleManager> 
    </system.web> 

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

String [] = роли Roles.GetRolesForUser (имя пользователя)

но контроллер с [Авторизовать (Roles = «Administrator»)] всегда перенаправляет страницу на экран входа, даже если имя пользователя и роль оценены.

Пожалуйста, помогите!

ответ

2

Я считаю, что я нашел источник вашей проблемы. Я предполагаю, что вы используете проверку подлинности Windows и пытаетесь использовать свой поставщик ролей вместо групп Windows, которые автоматически загружаются. Заглядывая в источник MVC AuthorizeAttribute, вы обнаружите, что на самом деле он вызывает Principal.IsInRole. В MSDN:

InRole сначала проверяет свойство IsRoleListCached, чтобы определить, доступен ли кешированный список имен ролей для текущего пользователя. Если свойство IsRoleListCached имеет значение true, кеш-лист проверяется на указанную роль. Если метод IsInRole находит указанную роль в кеш-списке, он возвращает true. Если IsInRole не определяет указанную роль, он вызывает метод GetRolesForUser экземпляра поставщика по умолчанию для определения того, связано ли имя пользователя с ролью из источника данных для настроенного значения ApplicationName.

Таким образом, я предполагаю, что, поскольку Принципал является Принципом Windows, он входит в его роли, заполненные и кэшированные. Когда IsInRole называется, он говорит: «Эй, я уже получил роль, то почему бы мне вернуться к провайдеру, чтобы получить их снова»

Что вы можете сделать вместо этого будет Somthing так:

protected void Application_PostAuthenticateRequest(object sender, EventArgs e) 
    { 
     WindowsIdentity identity = HttpContext.Current.Request.LogonUserIdentity; 
     HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new GenericIdentity(identity.Name), Roles.GetRolesForUser()); 
    } 

Это выведет идентификатор Windows из HttpContext, использует это имя для явного выбора ролей из вашего настраиваемого провайдера, а затем вместо этого добавит новый GenericPrincipal в запрос. Я пошел дальше и реализовал некоторую логику для хранения ролей в зашифрованный файл cookie, поэтому нам не нужно отправлять провайдера ролей по каждому запросу.

void Application_PostAuthenticateRequest() 
    { 
     HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName]; 
     FormsAuthenticationTicket authTicket; 
     if (authCookie == null || authCookie.Value == "") 
     { 
      string[] getRoles = Roles.GetRolesForUser(); 
      authTicket = new FormsAuthenticationTicket(1, 
       User.Identity.Name, 
       DateTime.Now, 
       DateTime.Now.AddMinutes(20), 
       true, 
       String.Join(";", getRoles)); 

      string encryptedTicket = FormsAuthentication.Encrypt(authTicket); 
      authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket); 
      HttpContext.Current.Response.Cookies.Add(authCookie); 
     } 
     try 
     { 
      authTicket = FormsAuthentication.Decrypt(authCookie.Value); 
     } 
     catch 
     { 
      return; 
     } 
     string[] roles = authTicket.UserData.Split(';'); 
     if (Context.User != null) 
      Context.User = new System.Security.Principal.GenericPrincipal(Context.User.Identity, roles); 
    } 
+0

Спасибо за ваш комментарий Дэн. Я смог сделать то же самое, кроме использования ClientPrincipal, которые перекрывают метод WindowsPrincipal IsInRole, чтобы обеспечить дополнительную функциональность. https://dotnetfiddle.net/sTU8EQ –

+0

То же самое здесь. Дэн, ты мужчина. Не знал об аутентификации сообщения, концепции обертывания WindowsPrincipal или о том, что по умолчанию impl просто использовал кешированные роли. – Visser

Смежные вопросы