Как создать CustomPrincipal глобально (с атрибутом AuthorizeAttribute и без него)

У меня есть пользовательский принцип/идентификатор для моего веб-приложения ASP.NET MVC4. Я также создал AuthorizeAttribute, чтобы создать экземпляр моего настраиваемого принципала, назначив его httpContext.User в контроллерах, где мне требуется аутентификация.

Это отлично работает для контроллеров/действий, которые были украшены моим AuthorizeAttribute, однако для контроллеров, которые не требуют аутентификации (но все равно используют его, если он есть), я хотел бы получить свой CustomPrincipal ( и предпочтительно через HttpContext.User).

В этих не оформленных контроллерах/действиях установлен HttpContext.User, но с GenericPrincipal, а не с моим CustomPrincipal. Где лучше всего "переопределить" значение по умолчанию для HttpContext.User для GenericPrincipal?

Кроме того, если это делается в каждом запросе с файлом auth, , как бы я тогда не выполнял работу дважды в случае декорированного контроллера AuthorizeAttribute (который тогда просто стал бы одним эта мандатная аутентификация).

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

Я думаю, что некоторые из вариантов (не определенные моей логикой позади каждого):

  • используйте сеанс (и обрабатывайте логику, чтобы создать то, что мне нужно здесь, забыв о Принципах).
  • Application_AuthenticateRequest - увиденные комментарии в Интернете, что это старая школа.
  • Пользовательские фильтры, установленные на базовом контроллере
  • Создайте атрибут AuthorizationAttribute на базовом контроллере, который позволяет всем и настраивает HttpContext.User, как я хочу.
  • IHttpModule - это похоже на спуск (и продвигается по этому пути, если другие не согласны).

Мысли?

Ответ 1

Вы можете использовать глобальный фильтр действий. Предположим, что у вас есть пользовательский принцип:

public class MyPrincipal : GenericPrincipal
{
    public MyPrincipal(IIdentity identity, string[] roles): base(identity, roles)
    {
    }

    ... some custom properties and stuff
}

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

public class GlobalIdentityInjector : ActionFilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var identity = filterContext.HttpContext.User.Identity;

        // do some stuff here and assign a custom principal:
        var principal = new MyPrincipal(identity, null);
        // here you can assign some custom property that every user 
        // (even the non-authenticated have)

        // set the custom principal
        filterContext.HttpContext.User = principal;
    }
}

Глобальный фильтр будет зарегистрирован в ~/App_Start/FilterConfig.cs, чтобы гарантировать, что он будет применяться ко всем действиям:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new GlobalIdentityInjector());
    }
}

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

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }

        // we know that at this stage we have our custom
        // principal injected by the global action filter
        var myPrincipal = (MyPrincipal)httpContext.User;

        // do some additional work here to enrich this custom principal
        // by setting some other properties that apply only to
        // authenticated users

        return true;

    }
}

и тогда у вас может быть 2 типа действий:

public ActionResult Foo()
{
    var user = (MyPrincipal)User;

    // work with the custom properties that apply only
    // to anonymous users

    ...
}

[MyAuthorize]
public ActionResult Bar()
{
    var user = (MyPrincipal)User;

    // here you can work with all the properties
    // because we know that the custom authorization
    // attribute set them and the global filter set the other properties

    ...
}

Ответ 2

Принцип переопределения в:

protected void Application_PostAuthenticateRequest(object sender, EventArgs e)

Вместо

protected void Application_AuthenticateRequest(object sender, EventArgs e)

В Global.asax.cs работал у меня в веб-приложении ASP