【问题标题】:ApiController override to access User.ClaimsApiController 覆盖以访问 User.Claims
【发布时间】:2016-08-19 09:10:37
【问题描述】:

我有一个从 ApiController 继承的基本 api 控制器。在其中,我将覆盖 ExecuteAsync 并希望使用存储在 Principal.Claims var 中的一些数据。但是在调用 base.ExecuteAsync() 之前它是空的,并且在调用它之后为时已晚。我没有看到其他可以覆盖的内容来帮助解决这个问题?

public class ApiControllerBase : ApiController
{
    public MyUser CurrentUser { get; set; }

    public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
    {
        // principal.claims is empty
        ClaimsPrincipal principal = (ClaimsPrincipal)RequestContext.Principal; // principal.claims is empty

        var rv = base.ExecuteAsync(controllerContext, cancellationToken);

        // principal.claims is now populated but the controller.action that inherits from this basecontroller has already fired and thrown an exception since CurrentUser is null.
        principal = (ClaimsPrincipal)RequestContext.Principal;
    }
}

【问题讨论】:

  • 您确定需要重写此方法吗?你想实现什么行为?
  • 我在管道中找不到另一种方法来覆盖。最终目标是在我的基础 ApiController 上拥有一些属性,以便继承控制器可以使用它们。我需要加载这些属性的信息在 Principal.Claims 集合中。

标签: c# model-view-controller asp.net-web-api asp.net-identity owin


【解决方案1】:

如果您需要访问控制器中的声明,您可以执行以下操作:

public class MyUser 
{
    private readonly ClaimsIdentity _identity;

    public SeaUser(ClaimsIdentity identity)
    {
        _identity= identity;
    }

    public IEnumerable<Claim> Claims { get { return _identity.Claims; } }
}

public abstract class BaseController : ApiController
{
    private MyUser _user;

    public new MyUser User
    {
        get 
        { 
            return _user ?? (_user = User.Identity != null 
                                  ? new MyUser((ClaimsIdentity)User.Identity) 
                                  : null); }
        }
    }
}

然后在需要的地方使用 User 属性。

假设请求进入ApiController作用域,操作顺序如下:

  • 调用了 ApiController 的 ExecuteAsync 方法。
  • 调用了 ApiController 的 Initialize 方法。
  • 已检索注册的操作选择器。
  • 已注册操作选择器的 SelectAction 方法被调用。 如果仅匹配一种操作方法,则管道继续。
  • 检索所选操作的所有注册过滤器。
  • 调用授权过滤器。授权过滤器可以
    决定让管道继续执行或 终止管道。
  • 如果授权过滤器没有终止请求,操作 执行参数绑定。
  • ApiController.ModelState 已设置。
  • 动作过滤器被调用。动作过滤器决定要么 让管道继续执行或终止管道。
  • 如果动作过滤器没有终止请求,注册动作 调用者已检索。
  • 注册的 Action Invoker 的 InvokeActionAsync 方法是 调用以调用选定的操作方法。

注意:如果从 Authorization Filters 执行到 action 方法执行过程中发生任何异常,都会调用异常过滤器。

在这两者之间还有一些事情发生,但这非常接近完整的视图。查看ApiController source code 了解更多信息。

【讨论】:

  • +1 这确实有效,但我想知道是否有办法像我在上面尝试做的那样实现它。
  • 我在答案中添加了一些注释。根据 WebAPI 执行管道 - 在调用 ExecuteAsync 之前无法获取主体,因为它将在此方法中初始化。
【解决方案2】:

这对我有用 (Thread.CurrentPrincipal)

public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
{
    var principal = Thread.CurrentPrincipal as ClaimsPrincipal;
    return base.ExecuteAsync(controllerContext, cancellationToken);
}

我不确定 IAppBuilder (Startup.Configuration) 中的注册顺序是否重要,但我首先注册的是 Authentication 中间件。

【讨论】:

  • 对我来说 principal.Claims.Any() 在运行基本实现之前返回 false。
  • 您可以在另一个阶段检索索赔吗?如果不是,您是否 100% 确定在您创建工单时添加了声明?
  • 是的。如果我改为 var rv = base.ExecuteAsync(controllerContext, cancelToken); principal.Claims // 现在有了值。返回房车;
【解决方案3】:

您可以覆盖 OnActionExecuting(),每次执行操作时都会调用它。

public override void OnActionExecuting(ActionExecutingContext context)
    {
        var user = context.HttpContext.User;
        //store user.Claims in property so inherited controllers have access
        base.OnActionExecuting(context);
    }

【讨论】:

  • System.Web.Http.ApiController 上不存在该方法
  • 哎呀,对不起。我的应用程序使用的是 ASP.Net Core 1.0,其中有一个新的 Controller 类
猜你喜欢
  • 1970-01-01
  • 2011-10-28
  • 1970-01-01
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多