代码之家  ›  专栏  ›  技术社区  ›  Andez

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

  •  1
  • Andez  · 技术社区  · 11 年前

    由于我对应用程序服务的实现进行了编码(目前),以及为依赖注入配置它们的方式,我对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>
    {
        ...
    }
    

    以上所有内容都与我选择的容器SimpleInjector进行了依赖注入。

    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容器?

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

    1 回复  |  直到 11 年前
        1
  •  2
  •   heymega    11 年前

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

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

    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 .

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

    希望这有帮助。

    推荐文章