用户(的IPrincipal)使用Web 2.1阿比和Owin上ApiController的构造函数暂无数据 [英] User (IPrincipal) not avaliable on ApiController's constructor using Web Api 2.1 and Owin

查看:652
本文介绍了用户(的IPrincipal)使用Web 2.1阿比和Owin上ApiController的构造函数暂无数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的网页API 2.1 Asp.Net身份2.我试图让我的ApiController的构造经过身份验证的用户(我用AutoFac注入我的依赖关系),但用户在构造函数显示为未通过身份验证调用。

I am Using Web Api 2.1 with Asp.Net Identity 2. I am trying to get the authenticated User on my ApiController's constructor (I am using AutoFac to inject my dependencies), but the User shows as not authenticated when the constructor is called.

我想获得用户,所以我可以生成任何数据库的写操作的审计信息。

I am trying to get the User so I can generate Audit information for any DB write-operations.

有几件事情我这样做,可以帮助对诊断:
结果我使用的 app.UseOAuthBearerTokens 与Asp.Net身份认证2.这意味着,我删除了 app.UseCookieAuthentication(新CookieAuthenticationOptions())自带默认情况下,当你创建一个新的网页API启用2.1项目,Asp.Net身份2

A few things I'm doing that can help on the diagnosis:
I am using only app.UseOAuthBearerTokens as authentication with Asp.Net Identity 2. This means that I removed the app.UseCookieAuthentication(new CookieAuthenticationOptions()) that comes enabled by default when you are creating a new Web Api 2.1 project with Asp.Net Identity 2.

WebApiConfig 我注入我的仓库:

builder.RegisterType<ValueRepository>().As<IValueRepository>().InstancePerRequest();

下面是我的控制器:

[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
    private IValueRepository valueRepository;

    public ValuesController(IValueRepository repo)
    {
        valueRepository = repo;
        // I would need the User information here to pass it to my repository
        // something like this:
        valueRepository.SetUser(User);
    }

    protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
    {
        base.Initialize(controllerContext);

        // User is not avaliable here either...
    }
}

但是,如果我检查构造器的User对象,这是我得到:
用户

But if I inspect the User object on the constructor, this is what I get:

认证工作,如果我不通过我的道理,它将与未经授权。如果我通过令牌和我试图从任何方法访问用户,将会对其进行认证和正确填充。它只是不上时,它被称为构造器显示

The authentication is working, if I don't pass my token, it will respond with Unauthorized. If I pass the token and I try to access the user from any of the methods, it is authenticated and populated correctly. It just doesn't show up on the constructor when it is called.

在我的 WebApiConfig 我使用:

public static void Register(HttpConfiguration config)
{
    config.SuppressDefaultHostAuthentication();
    config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

    // Web API routes
    config.MapHttpAttributeRoutes();

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

     // ... other unrelated injections using AutoFac
 } 



其他无关注射

我发现,如果我删除此行: config.SuppressDefaultHostAuthentication()用户填充在构造

时这正常吗?我怎样才能在构造函数中身份验证的用户?

Is this expected? How can I get the authenticated user on the constructor?

编辑:
作为里卡德建议我试图让在初始化方法的用户,但它仍然是暂无数据,给我在图像中所描述的同样的事情。

As Rikard suggested I tried to get the user in the Initialize method, but it is still not avaliable, giving me the same thing described in the image.

推荐答案

不知道这仍然是相关的,但我已经确切地同样的问题,因为你已经如上所述。我已经成功使用自定义OWIN中间件组件来解决它。

Don't know if this is still relevant, but I've had exactly the same problems, as you've described above. I've managed to solve it using custom OWIN middleware component.

我的应用程序结构的一些信息:

Some info about my application structure:


  • 使用MVC Web应用程序和的WebAPI在同一项目中(可能不是最好的选择,但我没有时间去改变它,因为大限将至;))

  • 使用AutoFac作为IoC容器

  • 实现的自定义ICurrentContext保存信息有关当前(在MVC和承载令牌身份验证中的WebAPI与CookieAuth),在需要的地方(控制器,BAL对象是注入登录的用户,等等。)

  • 使用的EntityFramework 6 DB访问

  • 转换ASP.NET身份使用INT键,而不是字符串(的 http://www.asp.net/identity/overview /扩展/改变,主键换用户 - 在ASPNET身份

  • Using MVC WebApp and WebAPI in same project (probably not the best option, but I have no time to change it, since deadline is approaching ;))
  • Using AutoFac as IoC container
  • Implemented custom ICurrentContext to hold information about currently logged on user (with CookieAuth in MVC and Bearer Token Auth in WebAPI), which is injected where needed (controllers, BAL objects, etc.)
  • Using EntityFramework 6 for Db access
  • Converted ASP.NET Identity to use int keys rather than string (http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity)

所以到码。这是我的ICurrentContext接口:

So on to the code. This is my ICurrentContext interface:

public interface ICurrentContext
{
     User CurrentUser { get; set; } // User is my User class which holds some user properties
     int? CurrentUserId { get; }
}

和实现它的:

public class DefaultCurrentContext : ICurrentContext
{
    public User CurrentUser { get; set; }

    public int? CurrentUserId { get { return User != null ? CurrentUser.Id : (int?)null; } }
}



我还创建了一个OWIN中间件组件:

I've also created an OWIN middleware component:

using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;

namespace MyWebApp.Web.AppCode.MiddlewareOwin
{
    public class WebApiAuthInfoMiddleware : OwinMiddleware
    {
        public WebApiAuthInfoMiddleware(OwinMiddleware next)
            : base(next)
        {
        }

        public override Task Invoke(IOwinContext context)
        {
            var userId = context.Request.User.Identity.GetUserId<int>();
            context.Environment[MyWebApp.Constants.Constant.WebApiCurrentUserId] = userId;
            return Next.Invoke(context);
        }
    }
}



有关该组件的一些信息: MyWebApp.Constants.Constant.WebApiCurrentUserId是我用来避免错别字自多个地方使用的一些字符串常量(你可以用你自己的)。 Basicly这个中间件做什么,是它会使目前的用户ID的OWIN环境字典,然后调用管道的下一个动作。

Some information about this component: MyWebApp.Constants.Constant.WebApiCurrentUserId is some string constant (you can use your own) that I've used to avoid typos since its used in more than one place. Basicly what this middleware does, is that it adds current UserId to the OWIN environment dictionary and then Invokes the next action in pipeline.

然后我创建使用*扩展声明包括OMC(OWIN中间件组件)到OWIN管道:

Then I've created Use* extension statement to include OMC (OWIN Middleware Component) into OWIN pipeline:

using System;
using Owin;

namespace MyWebApp.Web.AppCode.MiddlewareOwin
{
    public static class OwinAppBuilderExtensions
    {
        public static IAppBuilder UseWebApiAuthInfo(this IAppBuilder @this)
        {
            if (@this == null)
            {
                throw new ArgumentNullException("app");
            }

            @this.Use(typeof(WebApiAuthInfoMiddleware));
            return @this;
        }
    }
}

要使用此OMC,我'已经把使用*语句后立即使用*语句不记名令牌我Startup.Auth.cs里:

To use this OMC, I've put the Use* statement right after Use* statement for Bearer token inside my Startup.Auth.cs:

// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions); // This was here before

// Register AuthInfo to retrieve UserId before executing of Api controllers
app.UseWebApiAuthInfo(); // Use newly created OMC

现在这一原则的实际使用情况进行AutoFac的注册方法内(称为上在Web应用程序的启动一些引导代码;在我的情况下,这是启动类(Startup.cs),配置方法)里面我ICurrentContext实现,它是:

Now the actual usage of this principle was inside AutoFac's Register method (called on some bootstrap code at the start of web application; in my case this was inside Startup class (Startup.cs), Configuration method) for my ICurrentContext implementation which is:

private static void RegisterCurrentContext(ContainerBuilder builder)
{
    // Register current context
    builder.Register(c =>
    {
        // Try to get User's Id first from Identity of HttpContext.Current
        var appUserId = HttpContext.Current.User.Identity.GetUserId<int>();

        // If appUserId is still zero, try to get it from Owin.Enviroment where WebApiAuthInfo middleware components puts it.
        if (appUserId <= 0)
        {
            object appUserIdObj;
            var env = HttpContext.Current.GetOwinContext().Environment;
            if (env.TryGetValue(MyWebApp.Constants.Constant.WebApiCurrentUserId, out appUserIdObj))
            {
                appUserId = (int)appUserIdObj;
            }
        }

        // WORK: Read user from database based on appUserId and create appUser object.

        return new DefaultCurrentContext
        {
            CurrentUser = appUser,
        };
    }).As<ICurrentContext>().InstancePerLifetimeScope();
}

这方法被称为在那里我建立AutoFac的容器(因此类型的输入参数ContainerBuilder)。

This method is called where I build AutoFac's container (hence the input parameter of type ContainerBuilder).

这样,我得到了一个实现CurrentContext,不管用户是如何验证(通过MVC Web应用程序或Web API)。网页API在我的情况电话都是从一些桌面应用程序制造,但数据库和大多数的代码库是一样的MVC应用程序和Web API。

This way I got single implementation of CurrentContext, no matter how user was authenticated (via MVC Web Application or Web API). Web API calls in my case were made from some desktop application, but database and most of codebase were the same for MVC App and Web API.

不知道它的正确的路要走,但它已经为我工作。虽然我还是有点担心这将如何表现线程明智的,因为我不知道究竟如何使用HttpContext.Current内部API调用会表现。我读过的地方,OWIN字典用于每个请求的基础,所以我认为这是安全的做法。而且我也认为这是不那么整齐的代码,而是一个有点坏坏的黑客读取用户ID。 ;)如果有什么错误使用这种approcah,我会很感激这方面任何评论。我一直在strugling这一两个星期,这是我在一个地方获得用户ID最接近的(解决通过拉姆达从AutoFac ICurrentContext时)。

Don't know if its the right way to go, but it has worked for me. Although I am still a little concerned how would this behave thread-wise, since I don't know exactly how using HttpContext.Current inside API calls would behave. I've read somewhere that OWIN Dictionary is used per-request basis, so I think this is safe approach. And I also think that this isn't so neat code, but rather a little nasty hack to read UserId. ;) If there's anything wrong with using this approcah, I'd appreciate any comment regarding it. I've been strugling with this for two weeks now and this is the closest I got of getting UserId in one place (when resolving ICurrentContext from AutoFac through lambda).

请注意:只要有GetUserId的使用,它可以与原始GetUserId(返回字符串)执行替换。我使用GetUserId的原因是因为我已经重写ASP.NET在一定程度上对使用int,而不是字符串TKEY的。我此基础上下面的文章做:的 http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity

NOTE: Wherever there is usage of GetUserId, it can be replaced with original GetUserId (which returns string) implementation. The reason I'm using GetUserId is because I've rewritten ASP.NET to some extent for using ints instead of strings for TKey. I've done this based on following article: http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity

这篇关于用户(的IPrincipal)使用Web 2.1阿比和Owin上ApiController的构造函数暂无数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆