Sunday 17 July 2016

Фильтр авторизации ASP.NET MVC >= 5

Начиная с ASP.NET MVC 4 стало возможным применять обновленный фильтр авторизации класс AuthorizationFilter c проверкой подлинности ролей.

Этот фильтр реализует тип IAuthorizationFilter и определяет необходимость выполнения метода действия (например, выполнение проверки подлинности или проверки свойств запроса) с точки зрения безопасности.
Классы AuthorizeAttribute и RequireHttpsAttribute являются примерами фильтров проверки подлинности. Фильтры проверки подлинности выполняются перед выполнением любых других фильтров.
Вот, немножко сплагиатил с официальной документации о фильтрах. Теперь по сути:

Лично я использовал в своих проектах указанный фильтр, в том числе и для проверки подлинности на основании определенных ролей. Это, впрочем, самое главное новшество фильтра авторизации, которое появилось с 4-ой версией MVC.

Я был удовлетворен работой класса и, особенно не вникал в суть его работы :) до тех пор, пока не воспользовался новой системой авторизации и профилей Microsoft ASP.NET Identity 2.0. Тогда выяснилось, что класс AuthorizeAttribute полноценно использовать нельзя. Например, для проверка ролей класс взаимодействует с интерфейсом IPrincipal, который реализует собственный, отличающийся от Identity 2.0, интерфейс. Вывод - сочинить собственный фильтр авторизации. Далее речь пойдет об этом :)

Итак, с начало код фильтра:

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using System.Web;
using System.Web.Mvc;
 
namespace avk_commerce.web.Filters
{
    public class RoleAuthorizationAttribute : ActionFilterAttributeIActionFilter
    {
        public string Roles { getset; }
        #region вызывается перед обращением к методу действия
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {                                 
            bool Match = false;
            var user = filterContext.HttpContext.User.Identity;
            //роли, переданные через параметр
            string[] _Roles = Roles.Split(',');
            if (user.IsAuthenticated)
            {
                var UserManager = 
     filterContext.HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
                int UserId = user.GetUserId<int>();
                foreach (var Role in _Roles)
                {
                    Match = UserManager.IsInRole(UserId, Role);                    
                }
                if (!Match)
                {
                    filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
                }
            }
            else {
                filterContext.Result = new ViewResult { ViewName= "LoginNow" };
            }            
            base.OnActionExecuting(filterContext);
        }
        #endregion
    }
}
Как видим, класс переопределяет метод OnActionExecuting. Класс содержит свойство
Roles. Это список ролей через запятую. Мы в первую очередь проверяем прошел ли текущий 
пользователь авторизацию на сайте. Потом проверяем - соответствуют ли задекларированные
в методе контролера разрешенные роли, тем ролям, которые присвоены текущему пользователю.
Выводы сохраняем в локальной переменной Match. Далее в зависимости от значения в переменной 
Match принимаем решения. Как видите, все очень просто :)

Пример практического применение:

using System.Web.Mvc;
using avk_commerce.web.Filters;
using avk_commerce.web.Models;
 
namespace avk_commerce.web.Controllers.Admin
{
    [RoleAuthorization(Roles = CustomRoles.Admin)]
    [Culture]    
    public class AdminController : BaseController
    {
        #region Index
        public ActionResult Index()
        {
            var item = this.GetHomeData(null);
            return View(item);
        }
        #endregion
               
        #region Dispose
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                //base.db.Dispose();
            }
            base.Dispose(disposing);
        }
        #endregion
    }
}
 

No comments:

Post a Comment