Создание атрибута AuthorizeAttribute - что мне нужно знать?

Вот мои требования:

  • Я буду добавлять пользователей к N количеству ролей; определенных в базе данных.

  • Мне нужно защитить каждое действие контроллера с помощью моего атрибута authorize.

Например, веб-приложение проверит, принадлежит ли зарегистрированный пользователь к любой из этих двух ролей, и если они это сделают, я впустил их. Как я могу указать атрибут Authorize для извлечения пользовательских ролей из таблицу базы данных, которую я выбираю?

 [Authorize(Roles = "Admin, Technician")]
 public ActionResult Edit(int id)
 {
     return View();
 }

Я пробовал Google для разных страниц, но ни один из них не подходит для того, что мне нужно, и слишком сложно.

Если официальная документация имеет то, что мне тоже хотелось бы найти, поскольку я не видел ничего, что мог бы использовать.

Любые предложения?

Например, этот вопрос имеет очень чистый ответ, но я не знаю, завершено ли оно или нет что-то важное.

Управление ролью и разрешениями ASP.NET MVC3 → С назначением разрешения времени выполнения


Edit

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

http://msdn.microsoft.com/en-us/library/8fw7xh74.aspx

Ответ 1

Я пережил почти такой же сценарий за последние пару недель, чтобы это могло помочь кому-то еще в одной лодке. Мой сценарий - это приложение MVC4 в интрасети компании с пользователями, хранящимися в Active Directory. Это позволяет для проверки подлинности Windows давать единый вход, поэтому нет необходимости в проверке форм. Роли хранятся в базе данных Oracle. У меня три роли:

  • Readonly: все пользователи должны быть участниками этого доступа для доступа к приложению
  • Пользователь: создать новые рекорды
  • Администратор: редактирование и удаление записей

Я решил использовать app.net роль провайдера api для создания собственного 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);
        }
    }
}

Причиной для двух разных атрибутов является то, что я использую один для роли Readonly, для которого все пользователи должны быть членами, чтобы получить доступ к приложению. Я могу добавить это в методе RegisterGlobalFilters в Global.asax, что означает, что он автоматически применяется к каждому контроллеру:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new ReadonlyAuthorize());
}

Затем в CustomAuthorize я могу сделать более подробный подход и указать роли, которые я хочу, и применить к контроллеру или отдельному действию, например. ниже Я могу ограничить доступ к методу Delete для пользователей в роли администратора:

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

Ответ 2

Есть несколько способов справиться с этим. Метод Дарина и продувки (оба очень опытные люди - один из них - автор безопасности) достойны ссылкой, которую вы предоставили.

Одна вещь, на которую нужно обратить внимание, - это кеш. Если вы используете кеширование выходного кэша на стороне сервера, вы можете случайно кэшировать что-то для одного пользователя, который возвращается другому пользователю. Пожалуйста, смотрите:

Фильтры OutputCache и Authorize в MVC3

и

Почему я не могу комбинировать атрибуты [Authorize] и [OutputCache] при использовании кеша Azure (приложение .NET MVC3)?

и

Выполнение пользовательской аутентификации, авторизации и ролей MVC

для получения дополнительной информации об этом и способах обработки кеширования, если вы используете атрибут authorize.

Ответ 3

Вы можете использовать поставщик членства по умолчанию и поставщик ролей, а не реализовывать свои собственные, но вам также нужно будет использовать базу данных членства asp.net по умолчанию aspnetdb.mdf.

См. здесь для прочтения