Merhabalar herkese. Bu yazımda Asp.Net Mvc’nin kendi bünyesinde barındırmış olduğu Identity sisteminin Unity Dependency Injection yöntemiyle nasıl ayağa kaldırıldığını adım adım inceliyor olacağız. Ayrıca ek olarak, katmanlı mimari yapısını da böylelikle görmüş olacağız.
1.Adım Katmanları Oluşturmak
Katmanlara göz attığımızda her bir katmanın ayrı ayrı kendine has görevleri var. Unity Container konfigürasyonlarımızı .IoC adlı katmanda tutuyor olacağız.
Asp.Net Identity’nin IdentityDbContext ve gerekli entitylerini(User,Role,UserRole gibi) ben .Entities katmanında barındırıyorum. Böylece gerektiğinde her katman bu nesnelere erişebilir.
IdentityContext adlı nesnemizin kodları şu şekildedir ;
using Microsoft.AspNet.Identity.EntityFramework;
using SeizeTheDay.Entities.Identity.Entities;
using System.Data.Entity;
using System.Data.Entity.Migrations;
namespace SeizeTheDay.Entities.Identity
{
public class IdentityContext : IdentityDbContext<User>
{
public IdentityContext()
: base("IdendityConnection", throwIfV1Schema: false)
{
}
public static IdentityContext Create()
{
return new IdentityContext();
}
static IdentityContext()
{
Database.SetInitializer<IdentityContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().ToTable("UserInfoes");
modelBuilder.Entity<IdentityRole>().ToTable("Roles");
modelBuilder.Entity<IdentityUserRole>().ToTable("UserRoles");
modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaims");
modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogins");
modelBuilder.Entity<IdentityUser>().ToTable("Users");
modelBuilder
.Entity<IdentityRole>()
.Property(p => p.Name)
.HasMaxLength(255);
modelBuilder
.Entity<IdentityUser>()
.Property(p => p.UserName)
.HasMaxLength(256);
modelBuilder
.Properties()
.Where(p => p.PropertyType == typeof(string) &&
!p.Name.Contains("Id") &&
!p.Name.Contains("Provider"))
.Configure(p => p.HasMaxLength(255));
}
}
//For Mysql configuration
internal class Configuration : DbMigrationsConfiguration<IdentityContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
SetSqlGenerator("Devart.Data.MySql", new Devart.Data.MySql.Entity.Migrations.MySqlEntityMigrationSqlGenerator());
}
}
}
Örnek olması açısından örneğin bir User nesnesi ise IdentityUser adlı kalıtımdan inherit edilmiştir.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
namespace SeizeTheDay.Entities.Identity.Entities
{
public class User : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? BirthDate { get; set; }
public string Address { get; set; }
public string PhotoPath { get; set; }
public string FacebookLink { get; set; }
public string TwitterLink { get; set; }
public string SkypeID { get; set; }
public string Status { get; set; }
public bool? IsDefault { get; set; }
public bool? IsActive { get; set; }
public DateTime? LastLoginDate { get; set; }
public DateTime? RegisteredDate { get; set; }
public int? InsertBy { get; set; }
public DateTime? LastModified { get; set; }
public int? LastModifiedBy { get; set; }
public string CoverPhotoPath { get; set; }
public string UserCity { get; set; }
public int? CountryID { get; set; }
public int? UserTypeID { get; set; }
public string UserTask { get; set; }
public string TagUserName { get; set; }
public string TagColor { get; set; }
}
}
FirstName ile başlayan propertyleri ben ayrı bir UserInfo adlı bir tablo tutuyorum, o yüzden bu nesneleri oluşturdum siz oluşturmak istemiyorsanız nesnelerinin tümünü silebilirsiniz.
2.Adım Identity Managers Nesnelerini Oluşturmak
Bildiğiniz üzere Identity’nin kendi bünyesinde barındırdığı manager sınıfları mevcuttur. Bunlar ApplicationRoleManager, ApplicationSignInManager ve ApplicationUserManager olarak adlandırılır. Bu sınıfları ben .Business adlı katmanın içinde barındırıyorum ve bu sınıfları Unity Dependency Injection yardımıyla ayağa kaldırıyorum.
ApplicationUserManager burada bizim için önemli bir parametredir. Asp.Net Identity’nin default olarak oluşturduğu bu UserManager sınıfı bizim oluşturacağımız bu UserManager daha farklı olacaktır. UserManager sınıfının kodları aşağıdaki gibidir.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.DataProtection;
using SeizeTheDay.Entities.Identity.Entities;
using System;
namespace SeizeTheDay.Business.Concrete.IdentityManagers
{
public class ApplicationUserManager : UserManager<User>
{
//We are using this provider in Startup
public static IDataProtectionProvider DataProtectionProvider { get; set; }
public ApplicationUserManager(IUserStore<User> store) : base(store)
{
// Configure validation logic for usernames
this.UserValidator = new UserValidator<User>(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<User>
{
MessageFormat = "Your security code is {0}"
});
this.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<User>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
this.EmailService = new EmailService();
this.SmsService = new SmsService();
var dataProtectionProvider = DataProtectionProvider;
if (dataProtectionProvider != null)
{
IDataProtector dataProtector = dataProtectionProvider.Create("ASP.NET Identity");
this.UserTokenProvider = new DataProtectorTokenProvider<User>(dataProtector);
}
}
}
}
ApplicationUserManager sınıfımız bu şekildedir. Role ve SignIn sınıflarımız ise şu şekildedir ;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using SeizeTheDay.Entities.Identity.Entities;
using System.Security.Claims;
using System.Threading.Tasks;
namespace SeizeTheDay.Business.Concrete.IdentityManagers
{
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
// Configure the application sign-in manager which is used in this application.
public class ApplicationSignInManager : SignInManager<User, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(User user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
}
}
using System.Linq;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using SeizeTheDay.Entities.Identity;
using SeizeTheDay.Entities.Identity.Entities;
namespace SeizeTheDay.Business.Concrete.IdentityManagers
{
public class ApplicationRoleManager : RoleManager<Roles>
{
public ApplicationRoleManager(IRoleStore<Roles, string> roleStore)
: base(roleStore)
{
}
public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
return new ApplicationRoleManager(new RoleStore<Roles>(context.Get<IdentityContext>()));
}
}
}
Manager sınıflarımız bu şekildedir. Eğer buraya kadar anlaşılmayan bir nokta olursa lütfen çekinmeden benimle iletişime geçiniz. Her yiğidin yoğurt yiyişi farklıdır derler, katmanlı mimari sayesinde her bir nesneyi ait olduğu katmana yerleştirip, daha derli toplu bir mekanizma oluşturmayı hedefledim kendi çapımda 🙂
3.Adım Startup.Auth Konfigürasyonu
Bu adıma kadar önemli aşamalardan geçtik. Şimdi Startup.Auth dosyamızı ayarlamamız gerekmekte.
using System;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.DataProtection;
using Owin;
using SeizeTheDay.Business.Concrete.IdentityManagers;
using SeizeTheDay.Entities.Identity.Entities;
namespace SeizeTheDay
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
ApplicationUserManager.DataProtectionProvider = app.GetDataProtectionProvider();
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationSignInManager>());
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationRoleManager>());
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User>(
validateInterval: TimeSpan.FromMinutes(0),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
}
}
Bu kod bizim için önemli GetDataProtectionProvider’ı nesnesimi ApplicationUserManager’den elde ediyoruz.
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());
her bir istekte ApplicationUserManager servisimizi getiriyoruz.
4.Adım Unity Config Sınıfı Oluşturmak
Bu adım bizim için en kritik noktadır. Gerekli alt yapıyı bu sınıf yardımıyla sağlıyoruz.
UnityConfigMvc sınıfının kodları ;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using SeizeTheDay.Business.Abstract.MySQL;
using SeizeTheDay.Business.Concrete.IdentityManagers;
using SeizeTheDay.Business.Concrete.Manager.MySQL;
using SeizeTheDay.DataAccess.Abstract.MySQL;
using SeizeTheDay.DataAccess.Concrete.MySQL;
using SeizeTheDay.Entities.Identity;
using SeizeTheDay.Entities.Identity.Entities;
using System;
using System.Data.Entity.Core.Objects;
using System.Web;
using System.Web.Mvc;
using Unity;
using Unity.AspNet.Mvc;
using Unity.Injection;
using Unity.Lifetime;
using Xgteamc1XgTeamModel;
namespace SeizeTheDay.IoC
{
/// <summary>
/// Specifies the Unity configuration for the main container.
/// </summary>
public static class UnityConfigMvc
{
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 RegisterComponents()
{
var container = new UnityContainer();
RegisterTypes(container);
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
private static void RegisterTypes(IUnityContainer container)
{
//Asp.Net Identity Dependency Injection
container.RegisterType<IdentityContext>(new PerRequestLifetimeManager());
container.RegisterType<ApplicationSignInManager>(new PerRequestLifetimeManager());
container.RegisterType<ApplicationRoleManager>(new PerRequestLifetimeManager());
container.RegisterType<ApplicationUserManager>(new PerRequestLifetimeManager());
container.RegisterType<EmailService>();
container.RegisterType<IAuthenticationManager>(
injectionMembers: new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));
container.RegisterType<IRoleStore<Roles,string>, RoleStore<Roles,string,IdentityUserRole>>(
new InjectionConstructor(typeof(IdentityContext)));
container.RegisterType<IUserStore<Entities.Identity.Entities.User>, UserStore<Entities.Identity.Entities.User>>(
new InjectionConstructor(typeof(IdentityContext)));
//Examples for other interfaces and managers
container.BindInRequstScope<IForumPostService, ForumPostManager>();
container.BindInRequstScope<IForumPostDal,MyForumPostDal>();
container.BindInRequstScope<IForumService, ForumManager>();
container.BindInRequstScope<IForumDal, MyForumDal>();
}
public static void BindInRequstScope<T1, T2>(this IUnityContainer container) where T2 : T1
{
container.RegisterType<T1, T2>(new HierarchicalLifetimeManager());
}
}
}
Diğer interface’leriniz ve manager sınıflarınız için örnek Dependency Injection oluşturmuş bulunmaktayım, dilerseniz onlarıda incelersiniz.
4.Adım Global.asax Konfigürasyonu
Bu adımda ise Unity’de ki gerekli işlemlerimizi Global.asax’da yani bir diğer deyişle web sayfamız ilk açıldığında oluşturduğumuz injectionlar ayağa kalkacaktır.
using SeizeTheDay.IoC;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace SeizeTheDay
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Ninject Settings
//ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(new BusinessModule(), new AutoMapperModule()));
//Unity Settings
UnityConfigMvc.RegisterComponents();
}
}
}
UnityConfigMvc.RegisterComponents(); sayesinde servislerimizi ayağa kaldırıyoruz.
5.Adım Startup Konfigürasyonu
Bu adımı 3.adım olarak karıştırmayın, ikisi farklı birer sınıftır. Bu sınıfta namespace’in üzerine bu örnek kodu yerleştirmeyi unutmayınız.
[assembly: OwinStartup(typeof(SeizeTheDay.Startup))]
//SeizeTheDay benim projemin adı, sizde kendi projenizin ismini yazınız
6.Adım Identity Kullanımı
Son adımımız ise Asp.Net Identity manager nesnelerimizi bir controller’da kullanalım.
#region DefinitionofServices
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private ApplicationRoleManager _roleManager;
private IAuthenticationManager _authenticationManager;
public AccountController(ApplicationUserManager userManager,ApplicationSignInManager signInManager ,
ApplicationRoleManager roleManager, IAuthenticationManager authenticationManager )
{
_userManager = userManager;
_signInManager = signInManager;
_roleManager = roleManager;
_authenticationManager = authenticationManager;
}
Bu şekilde bir yapıyla gerekli manager nesnelerimizi inject ediyoruz. Bir sonraki yazımızda görüşmek üzere. Eğer bir sorunuz olursa lütfen çekinmeden benimle iletişime geçiniz. İyi kodlamalar 🙂