MVC 5 IoC和身份validation
我即将开始一个项目,我将使用MVC5。 但是,由于我想使用IoC并稍后重用我的用户表,并向其添加自定义内容,我发现很难看到我如何使用MVC5附带的新Identity框架。
我越来越关注基本forms的认证。 你有什么解决方案?
我的需求:
- 必须注入用户存储库/服务
- 用户存储库必须驻留在DAL中
- 用户存储库必须能够支持除EF之外的其他技术
- 使用OpenID和OAuth进行身份validation必须要轻松实现
- 必须安全
- 应该可以在其他项目中重复使用,例如。 WPF
我一直在寻找答案,但我看到的一切都是在控制器中硬编码的。
你是怎么解决的? 您是从头开始编写的,还是可以绑定到可以扩展到其他.NET平台的东西,如WCF和WPF?
以下代码直接来自默认ASP.NET MVC 5模板中的AccountController。 它首先做的是Bastard Injection
。
[Authorize] public class AccountController : Controller { public AccountController() : this( new UserManager( new UserStore( new ApplicationDbContext()))) { } public AccountController(UserManager userManager) { UserManager = userManager; } }
接受的答案将交给那个人,它向我展示他们所做的事情,其中包含了上述要求
由于这是.NET,因此标准的安全方法是在应用程序边界进行身份validation,并将身份validation信息转换为IPrincipal 。 MVC支持开箱即用。
如果您需要在身份validation期间获得的其他信息,您可以在组合根中收集它并使用它来组合您的服务。
例如,假设您需要在较低层中使用经过身份validation的用户的电子邮件地址。 任何需要用户电子邮件地址的类都可以简单地将其作为具体依赖项请求:
public class EmailThingy { private readonly string userEmail; public EmailThingy(string userEmail) { if (userEmail == null) throw new ArgumentNullException("userEmail"); this.userEmail = userEmail; } // other members go here... }
在ASP.NET MVC中,Composition Root是IControllerFactory。 IIRC,您可以从CreateController方法中提取身份validation数据,并使用它来组成对象图。
这些天,我以相同的方式使用IPrincipal:我将它作为依赖项注入,而不是依赖于Thread.CurrentPrincipal Ambient Context,因为当通过Constructor Injection一致地注入所有内容时,它更容易进行unit testing。
您可能有兴趣了解一下Thinktecture.IdentityServer.v2
https://github.com/thinktecture/Thinktecture.IdentityServer.v2 。 您的许多问题已经实施和封装。 如果你找不到你需要的东西,你将不得不考虑如何抽象所有这些问题并自己实现。
我最终决定实现IUserStore,IUserStore,IUserPasswordStore,IUserLoginStore,以便能够将UserRepository移动到它正确的位置DataAccess Layer。 但仍然获得Owin的安全性和新的身份框架。
它实现起来非常简单,并且不需要太多来抽象它。 这是UserStoreWrapper的味道
namespace qubis.booking.WebApp.App_Code.Identity { public class UserServiceWrapper : IUserStore, IUserPasswordStore , IUserLoginStore { public IUserRepository UserRepos { get; set; } // My own Interface. public UserServiceWrapper(IUserRepository userRepo) { UserRepos = userRepo; } public async Task CreateAsync(ApplicationUserWrapper user) { UserRepos.Insert(user.RealUser); } public async Task FindByIdAsync(string userId) { var appUser = UserRepos.FindByUserName(userId); ApplicationUserWrapper wrappedUser; if (appUser != null) { wrappedUser = new ApplicationUserWrapper(appUser); } else wrappedUser = null; return wrappedUser; }
在帐户控制器中我只是要求它注入:
public AccountController(UserManager userManager) { UserManager = userManager;{ AllowOnlyAlphanumericUserNames = false }; }
当我使用Ninject时,我就像在内核中设置它一样:
// // Load your modules or register your services here! // // The kernel. private static void RegisterServices(IKernel kernel) { kernel.Bind>().To(); kernel.Bind>().ToSelf(); }
要查看Identity框架结构,请参阅此文章。 http://www.asp.net/identity/overview/extensibility/implementing-a-custom-mysql-aspnet-identity-storage-provider
如果您只需要注入自定义UserStore实现, 本文可能会对您有所帮助
基本上你需要注入这个(取决于你是否想要使用角色,索赔等):
-
编写实现IUser接口的User类
public class IdentityUser : IUser { public IdentityUser(){...} public IdentityUser(string userName) (){...} public string Id { get; set; } public string UserName { get; set; } public string PasswordHash { get; set; } public string SecurityStamp { get; set; } }
-
编写实现IUserStore,IUserClaimStore,IUserLoginStore,IUserRoleStore和IUserPasswordStore的用户商店类
public class UserStore : IUserStore
, IUserClaimStore , IUserLoginStore , IUserRoleStore , IUserPasswordStore { public UserStore(){...} public Task CreateAsync(IdentityUser user){...} public Task FindByIdAsync(string userId){...} .. . }