由于我编码(目前)应用程序服务的实现方式以及它们为依赖注入配置的方式,我的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分钟间隔后自动更新。