我正在编写自定义身份验证和授权系统,在我的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
为空)。
我在哪里必须设置委托人才能在请求生命期内使其可用?
答案 0 :(得分:0)
这里的问题是:
对于请求(和控制器)生命周期,请查看此示例:
Request -> new Controller() -> controller.Action() -> Response -> Dispose()
这就是说,毫无疑问,在Thread.CurrentPrincipal
中设置HttpContext.Current.User
甚至TokenController
都不会起作用:请求已经结束,线程现已处理完毕。当您请求另一个动作/方法时,它是另一个完整的上下文,包含新线程和新的User / Identity / Principal对象!
想象一下,如果你有10个用户。如果User.Identity
未绑定到当前请求的上下文,则应返回哪个主体/标识?这就是为什么......
因此,您最好在此处实施ActionFilter
或AuthenticationFilter
,以便在同一请求上下文中将您的控制器 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)并存储所有内容你需要作为声明,使它真正有用和容易。它也适用于承载/令牌认证/授权!