由于我编写了(当前)我的应用程序服务的实现方式以及为依赖项注入配置它们的方式,我对我的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容器?
注意:我只想从数据库中获取一次用户信息 - 而不是每次调用控制器所需的信息。
答案 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
。
这意味着您不必一直查询数据库以获取所需的数据。声明将在代码中指定的30分钟间隔后自动更新。
希望这有帮助。