MVC 5.2 Cookie登录OWIN并注入经过身份验证的用户信息

时间:2015-08-20 21:41:54

标签: asp.net-mvc dependency-injection session-cookies owin

由于我编写了(当前)我的应用程序服务的实现方式以及为依赖项注入配置它们的方式,我对我的MVC应用程序有些不满。

我希望按照SOLID原则分离应用程序的各个层。

问题是,在其中一些服务中,构造函数需要一个IUserContext实例。 IUserContext包含有关登录用户的各种信息,并将传递给几个不同的层。

public class ProjectDataLoader : DataLoaderBase, IProjectDataLoader
{
    public ProjectDataLoader(IMyDbContext dbContext, IUserContext userContext)
        : base (dbContext, userContext)
    {
    }
    ...

    public IEnumerable<ProjectViewModel> Find(string filter = "")
    {
        ...
    }
}

IUserContext的实现:

public class AspNetUserContext : IUserContext
{
    ...
}

我可以在每个方法调用上传递IUserContext,但我觉得它属于构造函数。但这不是问题所在。

当我通过AccountController从登录页面登录时,通过OWIN管道调用MyAppSignInManager.SignInOrTwoFactor。此时我在会话中创建了一个新的AspNetUserContext实例:

HttpContext.Current.Session["UserContext"] = aspNetUserContext;

现在我有自定义SignInManager实现:

public class MyAppSignInManager : SignInManager<MyAppUser, string>
{
    ...
}

我有一个自定义的IUserStore实现:

public class MyAppUserStore : IUserPasswordStore<MyAppUser>,
    IUserStore<MyAppUser>
{
    ...
}

以上所有内容都已通过Simple Injector与我选择的容器连接起来。

public static class DependencyConfig
{
    public static Container Initialize(IAppBuilder app)
    {
        Container container = GetInitializeContainer(app);
        container.Verify();

        DependencyResolver.SetResolver(
            new SimpleInjectorDependencyResolver(container));

        return container;
    }

    private static Container GetInitializeContainer(IAppBuilder app)
    {
        var container = new Container();

        RegisterCommon(container);
        RegisterRepositories(container);
        RegisterDataLoaders(container);
        RegisterAppServices(container);
        RegisterMvc(app, container);

        return container;
    }

    private static void RegisterCommon(Container container)
    {
        container.Register<IUserContext>(() =>
        {
            IUserContext context = null;
            if (HttpContext.Current.Session == null)
                context = new AspNetUserContext(Guid.Empty, Guid.Empty);
            else
                context = (IUserContext)HttpContext.Current.Session["UserContext"];

            return context;

        }, Lifestyle.Transient);
    }

    private static void RegisterRepositories(Container container)
    {
        container.RegisterPerWebRequest<IUserRepository>(() =>
            new UserRepository(container.GetInstance<IMyApp4Context>()));

        container.Register<IMyApp4Context>(() => new MyApp4Context(),
            Lifestyle.Transient);
    }

    private static void RegisterDataLoaders(Container container)
    {
        container.Register<IProjectDataLoader, ProjectDataLoader>();
        container.Register<ContractDataLoader>();
        container.Register<DrawingDataLoader>();
        container.Register<WeldDataLoader>();
    }

    private static void RegisterAppServices(Container container)
    {
    }

    private static void RegisterMvc(IAppBuilder app, Container container)
    {
        container.RegisterSingle(app);
        container.RegisterPerWebRequest<MyAppUserManager>();
        container.RegisterPerWebRequest<SignInManager<MyAppUser, string>,
            MyAppAppSignInManager>();

        container.RegisterPerWebRequest(() =>
        {
            if (HttpContext.Current != null && 
                HttpContext.Current.Items["owin.Environment"] == null && 
                container.IsVerifying())
            {
                return new OwinContext().Authentication;
            }
            return HttpContext.Current.GetOwinContext().Authentication;
        });

        container.RegisterPerWebRequest<IUserStore<MyAppUser>>(() =>
            new MyAppUserStore(container.GetInstance<IUserRepository>()));

        app.UseOwinContextInjector(container);

        container.RegisterMvcControllers(
                Assembly.GetExecutingAssembly());

    }

    private static void InitializeUserManager(MyAppUserManager manager, IAppBuilder app)
    {
        manager.UserValidator =
         new UserValidator<MyAppUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

        manager.PasswordValidator = new PasswordValidator()
        {
            RequiredLength = 6,
            RequireNonLetterOrDigit = false,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
        };

        IDataProtectionProvider dataProtectionProvider =
             app.GetDataProtectionProvider();

        if (dataProtectionProvider != null)
        {
            manager.UserTokenProvider =
             new DataProtectorTokenProvider<MyAppUser>(
              dataProtectionProvider.Create(purposes: new string[] { "ASP.NET Identity" }));
        }
    }

}

还有:

public partial class Startup
{   
    public void ConfigureAuth(IAppBuilder app, Container container)
    {
        app.CreatePerOwinContext(() => container.GetInstance<MyAppUserManager>());

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString(value: "/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<MyAppUserManager, MyAppUser>(
                    validateInterval: TimeSpan.FromMinutes(value: 30),
                    regenerateIdentity: (manager, user) =>
                    {
                        return user.GenerateUserIdentityAsync(manager);
                    })
            }
        });
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
        app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
        app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
    }
}

然后将这些用于控制器:

public class ProjectController : MyBaseContextController
{
    public ProjectController(IProjectDataLoader loader)
        : base(context)
    {
        ...
    }
}

我最初的问题是如何在进行cookie身份验证后获取MyAppUser。也许问这个仍然有效。

更好的问题是问我想要完成什么。基本上我想要的是将IUserContext注入我的服务。这需要注入我的DI容器中注册的各种服务实现的构造函数中。但是,在用户登录/验证之前,此实例将不可用。

注意:所有用户信息都存储在SQL中,我使用Entity Framework来访问所有这些信息。

因此,一旦用户通过登录页面通过MyAppSignInManager.SignInOrTwoFactor方法以及cookie登录进行身份验证,我如何才能将我的AspNetUserContext(IUserContext)实例提供给我的DI容器?

注意:我只想从数据库中获取一次用户信息 - 而不是每次调用控制器所需的信息。

1 个答案:

答案 0 :(得分:2)

  

“我只想从数据库中获取一次用户信息。”

您应该考虑将所需的用户数据存储在声明中。

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString(value: "/Account/Login"),
    Provider = new CookieAuthenticationProvider
    {
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<MyAppUserManager, MyAppUser>(
            validateInterval: TimeSpan.FromMinutes(value: 30),
            regenerateIdentity: (manager, user) =>
            {
                return user.GenerateUserIdentityAsync(manager);
            })
    }
})

GenerateUserIdentityAsync方法会添加核心身份声明,但您可以覆盖此声明并存储您的服务所需的自定义声明。然后,您可以传入IClaimsIdentity

,而不是将IUserContext传递给您的服务

这意味着您不必一直查询数据库以获取所需的数据。声明将在代码中指定的30分钟间隔后自动更新。

希望这有帮助。