使用ASP.NET Identity和Autofac OWIN集成进行授权

joe*_*dev 6 asp.net asp.net-mvc autofac asp.net-web-api owin

(此问题的底部添加了更新)

我有一个Web应用程序,它使用MVC5和WebAPI2以及用于DI的Autofac.该应用程序使用ASP.NET身份和oAuth承载令牌,虽然后者可能不重要.这一切都运行得很好,但是在这一点上我需要在整个OWIN管道以及我的应用程序的其余部分共享我注入的服务的相同实例,因此我正在尝试为MVC和Web设置Autofac的OWIN集成API.我似乎特写一切似乎除了工作AuthorizeAttibutesApiControllers.oAuth进程成功完成,我最终使用不记名令牌登录,但随后尝试使用WebAPI控制器/操作上的所述令牌进行授权失败.

具体来说,在IsAuthorized方法中System.Web.Http.AuthorizeAttribute,IPrincipal.Identity似乎没有正确实例化,因为它没有适当的声明,并且IsAuthenticated属性总是错误的.Autofac的开发人员表示该属性应该与OWIN集成一起使用,即使该代码使用了GlobalConfiguration 不适合OWIN集成的代码.我已经看到了多个要删除的建议config.SuppressDefaultHostAuthentication()(这里这里),虽然这是不可取的,但我已经尝试了绝望但却无济于事 - 对于我的特定配置,这会导致IPrincipal返回null.我还尝试修改一个比我自己更简单的示例项目,以便AuthorizeAttribute在WebAPI控制器上使用,但也没有成功.在这一点上,我没有尝试的东西,非常感谢帮助.

这是我的Startup.cs:

[assembly: OwinStartup(typeof (Startup))]
namespace Project.Web
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var builder = new ContainerBuilder();
            builder.RegisterControllers(Assembly.GetExecutingAssembly());
            var config = new HttpConfiguration();
            builder.RegisterHttpRequestMessage(config);
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            RegisterGeneralTypes(builder);
            var container = builder.Build();
            WebApiConfig.Register(config);
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
            WebApiFilterConfig.RegisterGlobalFilters(config.Filters);

            app.UseAutofacMiddleware(container);
            app.UseAutofacWebApi(config);
            app.UseAutofacMvc();
            app.UseWebApi(config);

            ConfigureAuth(app);
        }

        private static void RegisterGeneralTypes(ContainerBuilder builder)
        {
            builder.Register(c => new DomainModelContext())
                .AsSelf()
                .InstancePerRequest();

            builder.Register(c => HttpContext.Current.User.Identity)
                .As(typeof (IIdentity));

            builder.RegisterType<EmailService>()
                .AsImplementedInterfaces()
                .InstancePerRequest();

            builder.Register(c => new IdentityFactoryOptions<DomainUserManager>
            {
                DataProtectionProvider = DataProtectionProvider
            }).InstancePerRequest();

            builder.RegisterType<DomainUserManager>()
                .AsSelf()
                .UsingConstructor(typeof (IIdentityMessageService),
                    typeof (IdentityFactoryOptions<DomainUserManager>),
                    typeof (CustomUserStore))
                .InstancePerRequest();

            builder.RegisterType<CustomUserStore>()
                .AsImplementedInterfaces()
                .AsSelf()
                .InstancePerRequest();

            builder.Register(c => HttpContext.Current.GetOwinContext().Authentication)
                .As<IAuthenticationManager>();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

和我的Startup.Auth.cs:

public partial class Startup
{
    internal static IDataProtectionProvider DataProtectionProvider;
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
    public static string PublicClientId { get; private set; }

    public void ConfigureAuth(IAppBuilder app)
    {
        var onValidateIdentity = SecurityStampValidator
            .OnValidateIdentity<DomainUserManager, DomainUser, int>(
                validateInterval: TimeSpan.FromMinutes(30),
                regenerateIdentityCallback: (manager, user) =>
                    user.GenerateUserIdentityAsync(manager, CookieAuthenticationDefaults.AuthenticationType),
                getUserIdCallback: id => id.GetUserId<int>());

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            LoginPath = new PathString("/account/login"),

            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = onValidateIdentity
            }
        });
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/v1/account/externallogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
        };

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

        DataProtectionProvider = app.GetDataProtectionProvider();
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为这涵盖了它,但我很乐意根据要求发布额外的代码.

UPDATE

所以根据jumuro的回答,我按照建议更改了我的注册顺序.但是,这只是将完全相同的问题从Web API授权转移到MVC授权.由于我在更新之前有MVC auth工作,我最终尝试在管道中注册两次auth,如下所示:

app.UseAutofacMiddleware(container);
ConfigureAuth(app);
app.UseAutofacWebApi(config);
app.UseAutofacMvc();
app.UseWebApi(config);
ConfigureAuth(app);
Run Code Online (Sandbox Code Playgroud)

这是有效的,但我真的不能说我理解为什么,我无法想象这样做两次是好的.所以现在我有了新的问题:

  1. 有意义的是,WebAPI首先需要在管道中注册Auth,但为什么MVC要求我最后注册Auth 呢?
  2. 我怎样才能清理它并避免ConfigureAuth两次通话?

jum*_*uro 6

您必须以正确的顺序将中间件添加到应用程序管道中.在MVC和Web Api中间件处理请求之前,必须验证承载令牌.

在您的Configuration()方法中尝试此顺序:

public void Configuration(IAppBuilder app)
{
    ...
    app.UseAutofacMiddleware(container);
    ConfigureAuth(app);
    app.UseAutofacMvc();
    app.UseWebApi(config);
    app.UseAutofacWebApi(config);
    ...
}
Run Code Online (Sandbox Code Playgroud)

我希望它有所帮助.