AspNet Identity and IoC Container Registration
https://github.com/trailmax/IoCIdentitySample
TL;DR: Registration code for Autofac, for SimpleInjector, for Unity.
Tony Mackay has an alternative walk-through of a very similar process but with Autofac
Warning: If you don’t know what Dependency Injection is or you don’t know why you need this, don’t waste your time on this article. This approach is not recommended for cases when you don’t need a IoC container and have only a handful of controllers. Visual Studio template with Identity framework works great out of the box. You don’t need DI to enjoy full power of Asp.Net Identity. This article does not explain what DI is, how it works and why you need it. So proceed with caution and only if you know the difference between constructor injection and lifetime scope.
I regularly monitor StackOverflow for questions related to AspNet Identity framework. And one question keeps popping up over and over again: how do you use Inversion of Control containers with this framework.
If you know the main principles of Dependency Injection, things are very simple. Identity is very DI-friendly and I’ve done a number of projects with Identity and injected all Identity components via Autofac or SimpleInjector. The principles behind DI are applicable to all the containers, despite the differences in API’s for registration.
For the sake of this blog-post I’ll start with the standard VS2013 template and use Unity container, just because I’ve never used Unity before. You can view registrations for SimpleInjector in my test-project.
Projects where I used Autofac are far too complex to show as an example and none of them are open-source, so Autofac fans will have to figure out themselves from my examples.
Create project, install dependencies
For this sample I’ve used standard MVC project template from Visual Studio 2013.
First thing I’ve done – updated all Nuget packages. Just to keep new project really up to date.
Next thing I do is install container nuget packages:
PM> Install-Package Unity.Mvc
In default template I don’t like the way Identity classes are all in one file and in App_Start folder, so I move them to/Identity folder and place a separate classes into separate folders. This is my preference, not really a requirement -) (Did you know with Reshrper you can place caret on class name and Ctr+R+O -> Move To Folder and get all the classes into separate files, into different folder)
Configure DI
Now, once we have installed Unity, let’s register components with it. I see that Unity nuget package have created 2 files for me in /App_Start: UnityConfig.cs and UnityMvcActivator.cs.
UnityConfig.cs looks like this (I’ve removed comments):
public class UnityConfig
{
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
public static void RegisterTypes(IUnityContainer container)
{
// TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>();
}
}
This is pretty standard class most containers have for registration, but I’ve never seen Lazy initialisation of the container before. I’ll keep it that way – when in Rome, do as Romans -) However I’ve got strong feeling that RegisterType method should be private, as it makes no sense to expose it to the world. And here the registrations of components should go. I’ll come back to this class later.
Now I’d like to have a look on another unity class UnityWebActivator:
using System.Linq;
using System.Web.Mvc;
using IoCIdentity;
using Microsoft.Practices.Unity.Mvc;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(UnityWebActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(UnityWebActivator), "Shutdown")]
namespace IoCIdentity
{
public static class UnityWebActivator
{
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
// TODO: Uncomment if you want to use PerRequestLifetimeManager
// Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
}
public static void Shutdown()
{
var container = UnityConfig.GetConfiguredContainer();
container.Dispose();
}
}
}
I’ve included namespaces and attribute, as these are pretty important here. The two assembly attributes come fromWebActivator project and tells the application to execute methods before application starts and on application shut-down.
Start() method calls to the GetConfiguredContainer() from UnityConfig class that we’ve seen earlier – this is just initiates the DI container with registered types.
Next two lines de-register standard MVC filter instead registers Unity filter provider. I presume this is to enable injections into filter objects.
This line
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Sets Unity container as MVC standard dependency resolver, so in your static methods you can goDependencyResolver.Current.GetService<MyService>(); and this way Unity will be called into action and will resolve an instance of type MyService.
I think this is good enough initialisation code. Let’s move on.
Register components
Now let’s register components we need. Despite the fact, that Unity can resolve concrete types without registration (as per comments I’ve deleted), I’ll register them anyway. Here is the basic list of registrations I have. This is not a complete list, I’ll add more types there when I need them:
private static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<ApplicationDbContext>();
container.RegisterType<ApplicationSignInManager>();
container.RegisterType<ApplicationUserManager>();
}
Now, lets look on end consumers of our components – controllers. I’m starting with AccountController as it has a use ofUserManager and ApplicationSignInManager. By default it looks like this:
public class AccountController : Controller
{
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
{
UserManager = userManager;
SignInManager = signInManager;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
private ApplicationSignInManager _signInManager;
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set { _signInManager = value; }
}
// this is hidden on the very bottom - go find it and delete later
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
// list of actions
Let’s follow the DI principles and remove all the crap. HttpContext.GetOwinContext().Get<ApplicationUserManager>() is a prime example of service locator as an anti-pattern. Also this does not use our Unity container, this uses Owin for object resolution. We don’t want to mix Owin registrations with Unity registrations for many reasons (the biggest reason is lack of lifetime management between 2 containers). So now my AccountController looks like this:
private readonly ApplicationUserManager userManager;
private readonly ApplicationSignInManager signInManager;
private readonly IAuthenticationManager authenticationManager;
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, IAuthenticationManager authenticationManager)
{
this.userManager = userManager;
this.signInManager = signInManager;
this.authenticationManager = authenticationManager;
}
// actions
If you noticed, previously we have not registered IAuthenticationManager with the container. Let’s do this now:
container.RegisterType<IAuthenticationManager>(
new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));
If you run your application now, you’ll get exception that “IUserStore can’t be constructed” – that is correct, we have not registered it anywhere and it is required for ApplicationUserManager. Register now IUserStore:
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new InjectionConstructor(typeof(ApplicationDbContext)));
I specify what type I’d like to use as a parameter, otherwise Unity will try to resolve DbContext which is wrong. But instead we need our ApplicationDbContext.
ApplicationUserManager class
Now let’s look on UserManager class. The constructor does not have much in it, only takes IUserManager. But there is static method Create that creates an instance of UserManager and configures the settings:
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
// configuration stuff here
}
This method creates instances of ApplicationUserManager. Let’s copy all the code from Create method into constructor:
public ApplicationUserManager(IUserStore<ApplicationUser> store, IdentityFactoryOptions<ApplicationUserManager> options)
: base(store)
{
// Configure validation logic for usernames
this.UserValidator = new UserValidator<ApplicationUser>(this)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
this.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
// Configure user lockout defaults
this.UserLockoutEnabledByDefault = true;
this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
this.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
this.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
this.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
this.EmailService = new EmailService();
this.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
this.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
}
Now we have constructor parameter IdentityFactoryOptions<ApplicationUserManager> options. Previously this was supplied by Owin when we executed this code:
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
This GetUserManager does magic tricks (I tried following their source code, but I’m not brainy enough-) and adds parameter you see above. But we’d like to avoid registration through Owin.
IdentityFactoryOptions<ApplicationUserManager> provides IDataProtectionProvider needed to generate user tokens to confirm account registration and for password reset tokens. It is used in constructor like this:
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
IDataProtector dataProtector = dataProtectionProvider.Create("ASP.NET Identity");
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtector);
}
We can’t resolve ourselves IdentityFactoryOptions, we need Owin for that. And we can’t resolve IDataProtectorourselves as well. But after looking on source code of Owin I have found where this dataProtector is coming from:IAppBuilder.GetDataProtectionProvider(). But IAppBuilder is not available anywhere apart from Startup.Auth.cs file. So we can resolve this class where we can and save as static reference. And then use this reference where needed:
public partial class Startup
{
// add this static variable
internal static IDataProtectionProvider DataProtectionProvider { get; private set; }
public void ConfigureAuth(IAppBuilder app)
{
// add this assignment
DataProtectionProvider = app.GetDataProtectionProvider();
// other stuff goes here unchanged
}
}
and in ApplicationUserManager I rewrite part with assigning UserTokenProvider like this:
// here we reuse the earlier assigned static variable instead of
var dataProtectionProvider = Startup.DataProtectionProvider;
// this is unchanged
if (dataProtectionProvider != null)
{
IDataProtector dataProtector = dataProtectionProvider.Create("ASP.NET Identity");
this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtector);
}
Now we can remove dependency of IdentityFactoryOptions<ApplicationUserManager> from ApplicationUserManager constructor. And the whole class now looks like this:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store)
{
// Configure validation logic for usernames
this.UserValidator = new UserValidator<ApplicationUser>(this)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
this.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
// Configure user lockout defaults
this.UserLockoutEnabledByDefault = true;
this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
this.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
this.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
this.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
this.EmailService = new EmailService();
this.SmsService = new SmsService();
var dataProtectionProvider = Startup.DataProtectionProvider;
if (dataProtectionProvider != null)
{
IDataProtector dataProtector = dataProtectionProvider.Create("ASP.NET Identity");
this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtector);
}
}
}
At this point the application should be usable and you should be able to register as a user, login and log out.
Clean-Up
Now that our app works with DI, let’s clean up some static calls that we don’t want around.
First of all, let’s fix ManageController, remove all empty controllers and initialisation of UserManager from OwinContext. The constructor should look like this:
private readonly ApplicationUserManager userManager;
private readonly IAuthenticationManager authenticationManager;
public ManageController(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
{
this.userManager = userManager;
this.authenticationManager = authenticationManager;
}
Now let’s go into Startup.Auth.cs and look on bits we don’t want:
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
I for sure know that Identity resolves ApplicationUserManager through Owin context and it uses it in certain cases. All the other Owin-resolved objects can be removed from this list:
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Then remove static object creation for UserManager and SignInManager to Unity resolution:
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());
And remove static methods ApplicationUserManager.Create and ApplicationSignInManager.Create – no longer needed or used anywhere.
By now you should have all Identity components injected into your controllers via Unity container. I was able to register and login into the application. So the goal of this article is complete.
For your reference full source code of the solution is available on GitHub
Posted in Uncategorized.Tagged asp.net, di, Identity, mvc, recipe.
AspNet Identity and IoC Container Registration的更多相关文章
- [ASP.NET MVC] 使用CLK.AspNet.Identity提供依权限显示选单项目的功能
[ASP.NET MVC] 使用CLK.AspNet.Identity提供依权限显示选单项目的功能 CLK.AspNet.Identity CLK.AspNet.Identity是一个基于ASP.NE ...
- 从Microsoft.AspNet.Identity看微软推荐的一种MVC的分层架构
Microsoft.AspNet.Identity简介 Microsoft.AspNet.Identity是微软在MVC 5.0中新引入的一种membership框架,和之前ASP.NET传统的mem ...
- AspNet Identity 和 Owin 谁是谁
英文原文:http://tech.trailmax.info/2014/08/aspnet-identity-and-owin-who-is-who/ 最近我发现Stackoverflow上有一个非常 ...
- Spring IOC Container原理解析
Spring Framework 之 IOC IOC.DI基础概念 关于IOC和DI大家都不陌生,我们直接上martin fowler的原文,里面已经有DI的例子和spring的使用示例 <In ...
- Microsoft.AspNet.Identity 自定义使用现有的表—登录实现
Microsoft.AspNet.Identity是微软新引入的一种membership框架,也是微软Owin标准的一个实现.Microsoft.AspNet.Identity.EntityFrame ...
- [ASP.NET MVC] 使用CLK.AspNet.Identity提供以角色为基础的访问控制(RBAC)
[ASP.NET MVC] 使用CLK.AspNet.Identity提供以角色为基础的访问控制(RBAC) 程序代码下载 程序代码下载:点此下载 前言 ASP.NET Identity是微软所贡献的 ...
- IOC Container(服务容器)的工作机制
IOC Container 是laravel的一个核心内容,有了IOC Container在Laravel的强大表现,我们可以在Laravel中实现很大程度的代码维护性.(文档我是看的懵逼懵逼的(*^ ...
- [.Net MVC] 用户角色权限管理_使用CLK.AspNet.Identity
项目:后台管理平台 意义:一个完整的管理平台需要提供用户注册.登录等功能,以及认证和授权功能. 一.为何使用CLK.AspNet.Identity 首先简要说明所采取的权限控制方式.这里采用了基于角色 ...
- 使用CLK.AspNet.Identity提供以角色为基础的访问控制(RBAC)
使用CLK.AspNet.Identity提供以角色为基础的访问控制(RBAC) 程序代码下载 程序代码下载:点此下载 前言 ASP.NET Identity是微软所贡献的开源项目,用来提供ASP.N ...
随机推荐
- C#使用委托进行异步编程。
首先引用MSDN中的一段话来描述一下如何使用异步方式.NET Framework 允许您异步调用任何方法. 为此,应定义与您要调用的方法具有相同签名的委托:公共语言运行时会自动使用适当的签名为该委托定 ...
- nefu 117 素数定理
小明是一个聪明的孩子,对数论有着很浓烈的兴趣.他发现求1到正整数10n 之间有多少个素数是一个很难的问题,该问题的难以决定于n 值的大小.现在的问题是,告诉你n的值,让你帮助小明计算小于10n的素数的 ...
- epon e8-c HG220GS超级密码破解
网上找了很多管理电信e8-c的破解资料,大多都是明文密码,而hg220gs则为加密的密码,找来找去最后终于找到加密方式了base64,真心不容易 下面从其他博文中转载过来留着记录 低端hack.主要是 ...
- 控件(进度类): RangeBase, Slider, ProgressBar, ProgressRing
1.RangeBase(基类) 的示例Controls/ProgressControl/RangeBaseDemo.xaml <Page x:Class="Windows10.Cont ...
- css-css权威指南学习笔记1
第一章 CSS和文档 1.结构化HTML:(个人理解) 1.内容.样式.行为分离 2.标签语义化,什么样的标签做什么样的事 3.符合web标准 4.结构清晰,header.content.footer ...
- 【BZOJ-2436】嘉年华 DP + 优化
2436: [Noi2011]Noi嘉年华 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 529 Solved: 382[Submit][Statu ...
- 【CQOI2016纯净整合】BZOJ-4519~4524 (6/6)
感觉CQOI的难度挺好的,比较贴近自身,所以拿出来做了一下 CQOI2016 Day1 T1:不同的最小割 涉及算法:最小割/分治/最小割树 思路: 最小割树裸题,直接分治最小割,记录下答案,最后排序 ...
- 公司内多个公众号实现账号互通(UnionID机制处理)
场景: 由于用户在每个公众号上的OpenID都不一样,如果要实现判断判断某个用户在其中一个公众号上已经绑定过,那么就要借助(UnionID机制)的机制. 条件: 1.拥有微信开放平台账号,且认证(ht ...
- 对接微信红包时:CA证书出错,请登录微信支付商户平台下载证书
今天在对接微信支付的微信红包发放时,出现““CA证书出错,请登录微信支付商户平台下载证书”的错误,特此记录一下: 如果你也在对接微信红包,并且你也在这个页面上下载了demo,再就是你也参照了里面的文档 ...
- Simultaneous Tag Editing in IntelliJ IDEA 14.1
If you're involved in web development and, for some reason, you haven't given a ride to IntelliJ IDE ...