如何正确设置原则以在请求生命期间检索它?

时间:2014-05-06 23:37:39

标签: asp.net asp.net-mvc asp.net-web-api asp.net-web-api2

我正在编写自定义身份验证和授权系统,在我的Web API 2项目中。

有一个TokenController,它有一个POST方法来接收用户凭据,验证并生成身份验证/授权令牌。

稍后,如果用户想要使用我的任何控制器,他们只需在查询字符串中传递令牌,例如:?token=xcd

我已经在我的控制器上设置了我的自定义授权属性,该属性从url检索此令牌并在我的数据库中测试它是否相同,如果有,他可以访问控制器。

但是,在TokenController中设置主体/身份并不能让它与其他控制器中的Thread.CurrentPrinciple一起使用。

我已经尝试了所有这些:

Thread.CurrentPrincipal = principal;
this.RequestContext.Principal = principal;
HttpContext.Current.User = principal;

当我尝试在授权阶段检索当前主体/身份时,不再有用户(User.Identity.Name为空)。

我在哪里必须设置委托人才能在请求生命期内使其可用?

1 个答案:

答案 0 :(得分:0)

这里的问题是:

  1. 您不了解控制器的生命周期。是的,他们有一个,它与请求有关。
  2. 您正在使用自定义授权/身份验证实施
  3. 对于请求(和控制器)生命周期,请查看此示例:

    Request -> new Controller() -> controller.Action() -> Response -> Dispose()
    

    这就是说,毫无疑问,在Thread.CurrentPrincipal中设置HttpContext.Current.User甚至TokenController都不会起作用:请求已经结束,线程现已处理完毕。当您请求另一个动作/方法时,它是另一个完整的上下文,包含新线程和新的User / Identity / Principal对象!

    想象一下,如果你有10个用户。如果User.Identity未绑定到当前请求的上下文,则应返回哪个主体/标识?这就是为什么......

    因此,您最好在此处实施ActionFilterAuthenticationFilter,以便在同一请求上下文中将您的控制器 BEFORE 称为设置身份/主体,以便随时可用于任何控制器。

    我们在这里创建了一个自定义身份验证框架。这并不难。以下是一些样本:

    public MyCustomAuthenticationFilter : FilterAttribute, IActionFilter
    {
       public void OnActionExecuted(ActionExecutedContext filterContext)
       {
          // You may set the user here.
       }
    
       public void OnActionExecuting(ActionExecutingContext filterContext)
       {
          // This will be called after the action has executed. Not that useful right now...
       }
    
       public class Override : FilterAttribute, System.Web.Mvc.Filters.IOverrideFilter
       { 
          public Type FiltersToOverride { get { return typeof(MyCustomAuthenticationFilter); } }
       }
    }
    

    然而,您可以将其注册为全局过滤器:

    GlobalFilters.Filters.Add(new MyCustomAuthenticationFilter());
    

    无论你不想想要执行它,你都可以使用过滤器覆盖:

    [MyCustomAuthenticationFilter.Override]
    public ActionResult Something()
    

    但是您需要自定义过滤器提供程序,它可以解析所有这些(和其他)覆盖,类似于:

    public class MyCustomFilterProvider : System.Web.Mvc.IFilterProvider
    {
        public IEnumerable<System.Web.Mvc.Filter> GetFilters(System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ActionDescriptor actionDescriptor)
        {
            var _globalFilters = System.Web.Mvc.GlobalFilters.Filters;
            var _controllerFilters = controllerContext.Controller.GetType().GetCustomAttributes(true).OfType<FilterAttribute>();
            var _actionFilters = actionDescriptor.GetCustomAttributes(true).OfType<FilterAttribute>();
    
            var _all = _globalFilters.Union(_controllerFilters).Union(_actionFilters).ToList();
            var _overrides = _all.Where(_f => _f.Instance.GetType().GetInterfaces().Any(_i => _i.Equals(typeof(System.Web.Mvc.Filters.IOverrideFilter)))).ToArray();
    
            foreach(var _override in _overrides)
            {
                var _found = _all.Where(_f => _f.Instance.GetType().Equals(((System.Web.Mvc.Filters.IOverrideFilter)_override.Instance).FiltersToOverride)).ToArray();
                foreach (var _item in _found) _all.Remove(_item);
            }
    
            return _all.Except(_overrides);
        }
    }
    

    此提供商应该注册工作(当然),所以:

    FilterProviders.Providers.Add(new MyCustomFilterProvider());
    

    我认为就是这样。不应该妨碍你。

    但是,您可以避免所有这一切,只需使用新的AspNet.Identity / Katana / Owin(如果这是问题,您不必使用EF)并存储所有内容你需要作为声明,使它真正有用和容易。它也适用于承载/令牌认证/授权!