MVC 5.2 Cookie Sign In 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, MyAppSignInManager从登录页面登录时。SignInOrTwoFactor通过OWIN管道调用。此时,我正在会话中创建一个新的AspNetUserContext实例:

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

现在我有自定义SignInManager实现:

public class MyAppSignInManager : SignInManager<MyAppUser, string>
{
    ...
}
我有一个自定义的IUserStore实现:
public class MyAppUserStore : IUserPasswordStore<MyAppUser>,
    IUserStore<MyAppUser>
{
    ...
}

上面所有的都已经连接到简单注入器的依赖注入(我选择的容器)。

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中,我使用实体框架来访问所有这些。

因此,一旦用户通过MyAppSignInManager通过登录页面登录通过身份验证。SignInOrTwoFactor方法,也通过一个cookie,我怎么能使我的AspNetUserContext (IUserContext)实例可用于我的DI容器?

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

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

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

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分钟间隔后自动更新。

最新更新