Последние пару недель я проходил по тому же сценарию, так что это может помочь кому-то еще в той же лодке.Мой сценарий - приложение MVC4 в корпоративной сети с пользователями, хранящимися в Active Directory.Это позволяет для аутентификации Windows давать единый вход в систему, поэтому не требуется аутентификация с помощью форм.Роли хранятся в базе данных Oracle.У меня есть 3 роли:
- Только для чтения: все пользователи должны быть участником этой группы, чтобы получить доступ к приложению
- Пользователь: Создать новые записи
- Администратор: Редактировать иудалить записи
Я решил использовать API поставщика роли asp.net для создания собственного AccountRoleProvider.Пока что мне нужно использовать только 2 метода: GetRolesForUser и IsUserInRole:
public class AccountRoleProvider : RoleProvider // System.Web.Security.RoleProvider
{
private readonly IAccountRepository _accountRepository;
public AccountRoleProvider(IAccountRepository accountRepository)
{
this._accountRepository = accountRepository;
}
public AccountRoleProvider() : this (new AccountRepository())
{}
public override string[] GetRolesForUser(string user521)
{
var userRoles = this._accountRepository.GetRoles(user521).ToArray();
return userRoles;
}
public override bool IsUserInRole(string username, string roleName)
{
var userRoles = this.GetRolesForUser(username);
return Utils.IndexOfString(userRoles, roleName) >= 0;
}
}
Я обновил web.config, чтобы использовать моего поставщика ролей:
<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="AccountRoleProvider">
<providers>
<clear/>
<add name="AccountRoleProvider"
type="MyApp.Infrastructure.AccountRoleProvider" />
</providers>
</roleManager>
Затем я создал 2настраиваемые атрибуты из AuthorizeAttribute, ReadOnlyAuthorize и CustomAuthorize.
ReadonlyAuthorize:
public class ReadonlyAuthorize : AuthorizeAttribute
{
private IAccountRepository _accountRepository;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User;
this._accountRepository = new AccountRepository();
if (!user.Identity.IsAuthenticated)
{
return false;
}
// Get roles for current user
var roles = this._accountRepository.GetRoles(user.Identity.Name);
if (!roles.Contains("readonly"))
{
return false;
}
return base.AuthorizeCore(httpContext);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
}
}
}
CustomAuthorize:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public string RedirectActionName { get; set; }
public string RedirectControllerName { get; set; }
private IAccountRepository _accountRepository;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User;
this._accountRepository = new AccountRepository();
var accessAllowed = false;
// Get the roles passed in with the (Roles = "...") on the attribute
var allowedRoles = this.Roles.Split(',');
if (!user.Identity.IsAuthenticated)
{
return false;
}
// Get roles for current user
var roles = this._accountRepository.GetRoles(user.Identity.Name);
foreach (var allowedRole in allowedRoles)
{
if (roles.Contains(allowedRole))
{
accessAllowed = true;
}
}
if (!accessAllowed)
{
return false;
}
return base.AuthorizeCore(httpContext);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
{
var values = new RouteValueDictionary(new
{
action = this.RedirectActionName == string.Empty ? "AccessDenied" : this.RedirectActionName,
controller = this.RedirectControllerName == string.Empty ? "Home" : this.RedirectControllerName
});
filterContext.Result = new RedirectToRouteResult(values);
}
}
}
Причина 2 разных атрибутов заключается в том, что я использую один для роли Readonly, что все пользователи должны быть участникамидля того, чтобы получить доступ к приложению.Я могу добавить это в метод RegisterGlobalFilters в Global.asax, что означает, что он применяется автоматически к каждому контроллеру:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new ReadonlyAuthorize());
}
Затем в CustomAuthorize я могу использовать более детальный подход и указать роли, которые я хочу и применить.к контроллеру или отдельному действию, например ниже, я могу ограничить доступ к методу удаления для пользователей в роли администратора:
[AccessDeniedAuthorize(RedirectActionName = "AccessDenied", RedirectControllerName = "Home", Roles = "Admin")]
public ActionResult Delete(int id = 0)
{
var batch = myDBContext.Batches.Find(id);
if (batch == null)
{
return HttpNotFound();
}
return View(batch);
}
Существуют дополнительные шаги, которые мне нужно предпринять, такие как обновление объекта User ролямитекущий пользователь является членом.Это будет извлекать роли для пользователя один раз, а не каждый раз в моих пользовательских атрибутах, а также использовать User.IsInRole.Нечто подобное должно быть возможно в Application_AuthenticateRequest в Gloal.asax:
var roles = "get roles for this user from respository";
if (Context.User != null)
Context.User = new GenericPrincipal(Context.User.Identity, roles);