درک مفاهیم اصلی ASP.NET MVC and Identity 2.0
دوشنبه 27 مرداد 1393در این مقاله سعی داریم تا مقدمات ASP.NET MVC and Identity 2.0 را دریابیم با component های اصلی سیستم و در کل با مولفه های جدیداشنا شویم
در بیستم مارچ سال 2014 تیم ASP.NET نسخه دوم Identity Frame work را منتشر کردند.
انتشار جدید همرا با تعدادی از مولفه های جدیدی همراه بود که از مدت ها قبل انتظار داشتیم و از نظر امنیت بسیار بالا و قابلیت دسترسی به همه نوع application،ASP.NETمورد توجه قرار گرفت.
Identity framework مولفه های پیشرفته ای مانند شبکه اجتماعی log-in و تعاریف کاربری گسترده را معرفی کرده است.
آموزش تصویری نصب و استفاده از Asp.Net Identity 2
انتشار جدید مولفه های زیر را معرفی کرده است :
1- تعریف حساب کاربری گسترده شده شامل Email و اطلاعات تماس
2- دو فاکتور اجازه دسترسی توسط ایمیل و SMS که از نظر کاربرد شبیه به آن چیزی است که Google و Microsoft استفاده می کنند.
3- تایید حساب توسط ایمیل
4- مدیریت کردن کاربران و نقش ها
5- قفل کردن حساب در پاسخ به تلاش های اشتباه برای log-in
Security Token Provider - 6 برای گسترش security token کاربر در پاسخ به تغییرات در تنظیمات امنیتی
7- پشتیبانی پیشرفته از login های اجتماعی
8- ادغام آسان درخواست ها بر اساس مجوز
Identity 2.0 یک فرایند تغییر به منظور بهتر شدن از نسخه اورجینال که سال پیش معرفی شده بود را توسط تعداد بیشمار مولفه های جدید که کمی پیچیدگی به همراه دارد را به نمایش می گذارد.
اگر شما مانند من اخیرا قدم در مسیر Identity framework گذاشته اید،اماده باشید در حالی که نیازی نیست تا برای بهتر شدن از ابتدای چیزی شروع کنید که قبلا وجود نداشته اما باید چیزهای بسیاری یاد بگیرید.
در این مقاله سعی داریم تا به اطراف نگاهی بیندازیم،با component های اصلی سیستم اشنا شویم و در کل با مولفه های جدید آشنا شویم
ما هنوز سعی نداریم تا زیاد وارد جزییات شویم.به این مقاله به دید یک تور آشنایی نگاه کنید.
در حالی که با کدهای زیادی روبرو هستیم اما هنوز نیازی نیست تا کاملا جزییاتش را درک کنیم.
فقط با مفاهیم کلی که component های اصلی کجا قرار گرفته اند و اینکه چگونه ساخت یافته اند اشنا شوید.
Identity 2.0 تغییرات Breaking را معرفی می کند:
Identity 2.0 وارد مکانی برای application های نوشته شده توسط نسخه یک نمی شود.
امکانات بیشتری در جهت تغییرات مهم مورد نیاز در معماری و طریقی که Identity API در application استفاده می شود وجود دارد.
بروز رسانی application ، ASP.NE از Identity 1.0 به نسخه دوم آن نیازمند کدهای جدیدی است که این خود فراتر از محدوده این مقاله است.
در ضمن آگاه باشید که حرکت از Identity 1.0 به نسخه دوم ساده نیست.
گرفتن مثال ها از Nuget:
بر طبق این مقاله تمپلیت قابل دسترس مستقیمی در پروژه ASP.NET MVC با استفاده از Identity 2.0 وجود ندارد.
برای این روند شما نیازمندید تا کتابخانه های پروژه مثال را به یک پروژه خالی ASP.NET MVC وارد کنید.
در ابتدا یک پروژه جدید ASP.NET ایجاد کنید و تمپلیت Empty را از میان
گزینه های نمایش داده شده انتخاب کنید.
به محض اینکه یک پروژه Empty خلق کردید،شما می توانید نمونه پروژه Identity 2.0 را از Nuget با استفاده از وارد کردن کد زیر در Packag Manager Console داشته باشید.
PM>Install-Package Microsoft.AspNet.Identity.Samples -Pre
وقتی عملیات نصب انجام شد، شما باید folder structure را در Solution Explorer که کاملا شبیه به یک پروژه استاندارد MVC است ببینید.
Nuget تمام چیزهای مورد نیاز برای ایجاد یک پروژه کامل ASP.NET MVC را شامل Models, Views, Controllers و بسیاری از component ها را که برای اجرا شدن application مورد نیاز است را دارا می باشد.
اگر چه در نگاه اول component های پروژه بسیار آشنا به نظر می ایند اما با یک نگاه دقیق تغییرات اساسی و همچنین پیچیدگی های اضافه شده نمایان می شود .
پیکر بندی Identity 2.0 :
به عقیده من یکی از نقاط قوت نسخه اصلی Identity framework سادگی ان بود.سادگی Identity version 1.0 استفاده از آن را بسیار آسان و متقابلا قابل درک کرده بود.
برای گرفتن ایده ما یک نگاه سریع به تنظیماتی می اندازیم که در زمان شروع application ،اجرا می شود و ان را با کد قابل مقایسه application ی که توسط Identity Version 1.0 استفاده میشد مقایسه می کنیم.
ما در هر دو نوع از پروژه در روت پروژه می توانیم فایل کلاسی با نام Startup.cs پیدا کنیم.
در این فایل کلاسی به نام Startup تعریف شده است و متد ConfigureAuth
()
صدا زده شده است، اما درواقع دیده نمی شود و این به این خاطر است که برخی از کدهای کلاس
Startup در partial class که در فولدر App_Start است تعریف شده است .
فایل کد Startup.Auth.cs نامیده شده است اما اگر ان را باز کنیم تعریف partial class استاندارد را که شامل متد ConfigureAuth()
پیدا می کنیم.
در پروژه ای که از نسخه 1.0 Identity Frameworkاستفاده کرده کدهای استاندارد برای متد ConfigureAuth()
این گونه است.
public partial class Startup { public void ConfigureAuth(IAppBuilder app) { // Enable the application to use a cookie to // store information for the signed in user app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login") }); // Use a cookie to temporarily store information about a // user logging in with a third party login provider app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); // Uncomment the following lines to enable logging // in with third party login providers //app.UseMicrosoftAccountAuthentication( // clientId: "", // clientSecret: ""); //app.UseTwitterAuthentication( // consumerKey: "", // consumerSecret: ""); //app.UseFacebookAuthentication( // appId: "", // appSecret: ""); //app.UseGoogleAuthentication(); } }
در مقایسه وقتی متد ConfigureAuth() را در پروژه مان با استفاده از
identity Version 2.0 می بینیم کدهای بیشتری اضافه شده است.
را در پروژه مان با استفاده از
public partial class Startup { public void ConfigureAuth(IAppBuilder app) { // Configure the db context, user manager and role // manager to use a single instance per request app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create); // Enable the application to use a cookie to store information for the // signed in user and to use a cookie to temporarily store information // about a user logging in with a third party login provider // Configure the sign in cookie app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), Provider = new CookieAuthenticationProvider { // Enables the application to validate the security stamp when the user // logs in. This is a security feature which is used when you // change a password or add an external login to your account. OnValidateIdentity = SecurityStampValidator .OnValidateIdentity<ApplicationUserManager, ApplicationUser>( validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) } }); app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); // Enables the application to temporarily store user information when // they are verifying the second factor in the two-factor authentication process. app.UseTwoFactorSignInCookie( DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5)); // Enables the application to remember the second login verification factor such // as phone or email. Once you check this option, your second step of // verification during the login process will be remembered on the device where // you logged in from. This is similar to the RememberMe option when you log in. app.UseTwoFactorRememberBrowserCookie( DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); // Uncomment the following lines to enable logging in // with third party login providers //app.UseMicrosoftAccountAuthentication( // clientId: "", // clientSecret: ""); //app.UseTwitterAuthentication( // consumerKey: "", // consumerSecret: ""); //app.UseFacebookAuthentication( // appId: "", // appSecret: ""); //app.UseGoogleAuthentication(); } }
در بالا اولین چیزی که توجه ما را جلب می کند فراخوانی های متعدد ازapp.CreatePerOwinContext
است که ما Call back هایش را ثبت می کنیم تا برای ایجاد نمونه هایی از نوع خاص به وسیله نوع ارگومان ها فراخوانی شوند.حالا نمونه های ایجاد شده قادرند تا از متد context.Get()
استفاده کنند.
در کل چیزی که به ما گفته می شود این است که برای پروژه مثالمان که توسط تیم Identity 2.0 مطرح شده Owin الان قسمتی از application ماست .در واقع نمی توان با اطمینان گفت که Owin برای Identity 2.0 ضروری است اما می توان گفت برای پروژه مثال ما اینگونه است.
ما همچنین می توانیم فراخوانی های دیگری را نیز در بدنه متد ConfigureAuth
ببینیم که دو فاکتور اجازه دسترسی را قرار داده اند و همچنین کدهای پیکربندی اجازه دسترسی کوکی که در نسخه قبلی دیده نمی شد.
قبل از پرداختن به IdentityConfig.cs بهتر است به ApplicationUser
class که در فولدر Models تعریف شده نگاهی بیندازیم.
ApplicationUser Class in Identity 2.0:
اگر قبلا application ی با نسخه قبلی Identity framework ایجاد کرده اید شاید در موقعیتی قرار گرفته اید که هسته کلاس IdentityUser
را محدود دیده باشید.
نسخه اول کلاس IdentityUser
:
public class IdentityUser : IUser { public IdentityUser(); public IdentityUser(string userName); public virtual string Id { get; set; } public virtual string UserName { get; set; } public virtual ICollection<IdentityUserRole> Roles { get; } public virtual ICollection<IdentityUserClaim> Claims { get; } public virtual ICollection<IdentityUserLogin> Logins { get; } public virtual string PasswordHash { get; set; } public virtual string SecurityStamp { get; set; } }
نسخه دوم کلاس Identitty 2.0
public class ApplicationUser : IdentityUser { public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> 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; } }
ما می بینیم که ApplicationUser
به عنوان sub-class، IdentityUser
مشخص شده است.بنابراین اگر به دنبال تعریف IdentityUser
برویم میبینیم که IdentityUser
همان طور که در dentity 2.0 framework تعریف شده خودش sub-class ،IdentityUser<TKey, TLogin, TRole, TClaim>
است.
IdentityUser Implementation from Identity 2.0:
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey> where TLogin : Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<TKey> where TRole : Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole<TKey> where TClaim : Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim<TKey> { public IdentityUser(); // Used to record failures for the purposes of lockout public virtual int AccessFailedCount { get; set; } // Navigation property for user claims public virtual ICollection<TClaim> Claims { get; } // Email public virtual string Email { get; set; } // True if the email is confirmed, default is false public virtual bool EmailConfirmed { get; set; } // User ID (Primary Key) public virtual TKey Id { get; set; } // Is lockout enabled for this user public virtual bool LockoutEnabled { get; set; } // DateTime in UTC when lockout ends, any // time in the past is considered not locked out. public virtual DateTime? LockoutEndDateUtc { get; set; } // Navigation property for user logins public virtual ICollection<TLogin> Logins { get; } // The salted/hashed form of the user password public virtual string PasswordHash { get; set; } // PhoneNumber for the user public virtual string PhoneNumber { get; set; } // True if the phone number is confirmed, default is false public virtual bool PhoneNumberConfirmed { get; set; } // Navigation property for user roles public virtual ICollection<TRole> Roles { get; } // A random value that should change whenever a users // credentials have changed (password changed, login removed) public virtual string SecurityStamp { get; set; } // Is two factor enabled for the user public virtual bool TwoFactorEnabled { get; set; } // User name public virtual string UserName { get; set; } }
همان طور که در بالا می بینید تمام property ها مرتبط با اجازه دسترسی و امنیت است و به نیازهای تجاری ما برای اطلاعات کاربر مربوط نیست.بنابراین فیلدهای Email
و PhoneNumber
مسیر طولانی را در جهت کوچک کردن نیاز برای شخصی سازی کلاس ApplicationUser
طی می کنند.
ورژن جدید IdentityUser
نوعی از ارگومان ها را برای اضافه کردن انعطاف پذیری بیشتر قرار میدهد.
مثلا در نسخه قبل برای تعریف متغیر Id
از نوع string بود اما در نسخه جدید در اینجا نوع ارگومان Tkey
به ما اجازه می دهد تا نوع فیلد Id
را تعیین کنیم .
تعریف متغیر Id:
public virtual TKey Id { get; set; }
تعریف متغیر Roles:
public virtual ICollection<TRole> Roles { get; }
ما می بینیم که نوع TRole
در زمان کامپایل بازگذاشته شده است .اگر به محدودیت نوع در این تعریف نگاه کنیم میبینیم که TRole
به نوع IdentityUserRole<TKey>
محدود شده که با نسخه یک چندان تفاوتی ندارد.ان چیزی که متفاوت است و نقطه عطف است تعریف IdentityUserRole
است.
در نسخه اول تعریف IdentityUserRole
این گونه بود:
public class IdentityUserRole { public IdentityUserRole(); public virtual IdentityRole Role { get; set; } public virtual string RoleId { get; set; } public virtual IdentityUser User { get; set; } public virtual string UserId { get; set; } }
در مقایسه با نسخه دو که این چنین است:
<public class IdentityUserRole<TKey { public IdentityUserRole(); public virtual TKey RoleId { get; set; } public virtual TKey UserId { get; set; } }
ببینید چه اتفاقی افتاد؟در اولی شامل referenceهایی به شی IdentityRole
است.
در نسخه دوم فقط شامل مقدار Id
است.در ادامه ما نگاهی می اندازیم به انعطاف پذیری که توسط IdentityUser
صورت می گیرد.در نطر بگیرید اگرچه تعریف کلاس پیچیده می شود اما در عوض بسیار انعطاف پذیر خواهد شد.
پیکربندی Components and Helpers در Identity 2.0:
زمانی که متد ConfigAuth()
از کلاس Startup
جایی هست که پیکربندی زمان اجرا برای شناسایی اتفاق می افتد.
ما در واقع از component های تعریف شده در فایل IdentityConfig.cs استفاده می کنیم تا اینکه چگونه بیشتر مولفه های Identity 2.0 در application رفتار می کنند را پیکر بندی کنیم.
اگر ما محتوای فایل IdentityConfig.cs را بررسی کنیم متوجه می شویم که تعداد زیادی کلاس وجود دارد.
در اینجا ما هر کلاس را به طور مستقل بررسی می کنیم و این در حالی است که همه انها دارای location مشترکی در فایل پروژه مان هستند.
تمام این کلاس ها در فضای نام ApplicationName.Models
تعریف نشده است.
Application User Manager and Application Role Manager:
The Identity 2.0 Application User Manager Class:
public class ApplicationUserManager : UserManager<ApplicationUser { public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store) { } public static ApplicationUserManager Create( IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) { var manager = new ApplicationUserManager( new UserStore<ApplicationUser>( context.Get<ApplicationDbContext>())); // Configure validation logic for usernames manager.UserValidator = new UserValidator<ApplicationUser>(manager) { AllowOnlyAlphanumericUserNames = false, RequireUniqueEmail = true }; // Configure validation logic for passwords manager.PasswordValidator = new PasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = true, RequireDigit = true, RequireLowercase = true, RequireUppercase = true, }; // Configure user lockout defaults manager.UserLockoutEnabledByDefault = true; manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5); manager.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 in here. manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser> { MessageFormat = "Your security code is: {0}" }); manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser> { Subject = "SecurityCode", BodyFormat = "Your security code is {0}" }); manager.EmailService = new EmailService(); manager.SmsService = new SmsService(); var dataProtectionProvider = options.DataProtectionProvider; if (dataProtectionProvider != null) { manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>( dataProtectionProvider.Create("ASP.NET Identity")); } return manager; } public virtual async Task<IdentityResult> AddUserToRolesAsync( string userId, IList<string> roles) { var userRoleStore = (IUserRoleStore<ApplicationUser, string>)Store; var user = await FindByIdAsync(userId).ConfigureAwait(false); if (user == null) { throw new InvalidOperationException("Invalid user Id"); } var userRoles = await userRoleStore .GetRolesAsync(user) .ConfigureAwait(false); // Add user to each role using UserRoleStore foreach (var role in roles.Where(role => !userRoles.Contains(role))) { await userRoleStore.AddToRoleAsync(user, role).ConfigureAwait(false); } // Call update once when all roles are added return await UpdateAsync(user).ConfigureAwait(false); } public virtual async Task<IdentityResult> RemoveUserFromRolesAsync( string userId, IList<string> roles) { var userRoleStore = (IUserRoleStore<ApplicationUser, string>) Store; var user = await FindByIdAsync(userId).ConfigureAwait(false); if (user == null) { throw new InvalidOperationException("Invalid user Id"); } var userRoles = await userRoleStore .GetRolesAsync(user) .ConfigureAwait(false); // Remove user to each role using UserRoleStore foreach (var role in roles.Where(userRoles.Contains)) { await userRoleStore .RemoveFromRoleAsync(user, role) .ConfigureAwait(false); } // Call update once when all roles are removed return await UpdateAsync(user).ConfigureAwait(false); } }
عملیاتی که انجام می شود شامل اضافه کردن کاربر جدید،اضافه کردن کاربر به نقش پاک کردن کاربر از نقش است.
ApplicationUserManager از کلاس UserManager<ApplicationUser>
گرفته شده است.بنابراین تمام کارایی های تعریف شده در UserManager
در ApplicationUserManager
نیز وجود دارد
یک متد استاتیک Create()
تعریف شده که یک نمونه از ApplicationUserManager
را برمی گرداند.
در این متد است که بیشتر تنظیمات پیکربندی کاربرتان مشخص می شود .
درون متد Create()
فراخوانی به context.Get<ApplicationDBContext>()
صورت می گیرد.
فراخونی context.Get<ApplicationDbContext>()
عملیات
call back
را انجام
می دهد.
اگر با دقت نگاه کنیم شما می توانید ببینید که در متد creat قبل از اینکه یک نمونه از ApplicationUserManager
به فراخواننده برگردد اجازه دسترسی کاربر و تنظیمات مدیریتی و پیش فرض ها در متد creat ست می شود.
تقریبا بسیاری از تنظیمات قابل درک است.
The Application Role Manager Class:
public class ApplicationRoleManager : RoleManager<IdentityRole { public ApplicationRoleManager(IRoleStore<IdentityRole,string roleStore) : base(roleStore) { } public static ApplicationRoleManager Create( IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context) { var manager = new ApplicationRoleManager( new RoleStore<IdentityRole>( context.Get<ApplicationDbContext>())); return manager; } }
همانند ApplicationUserManager
می توانید ببینید که ApplicationRoleManager
از RoleManager<IdentityRole>
برگرفته شده است و تمام functionality ان را داراست.
و همین طور می بینید که متد استاتیک Create()
یک نمونه از کلاس را برمی گرداند.
و همین طور می بینید که متد استاتیک Create()
یک نمونه از کلاس را برمی گرداند.
سرویس ایمیل و اس ام اس برای اعتبار سنجی حساب:
در فایل IdentityConfig.cs دو سرویس کلاس هستند:
EmailService -1
2- SmsService
The ASP.NET Identity Email Service Class:
public class EmailService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Plug in your email service here to send an email. return Task.FromResult(0); } }
The ASP.NET Identity SmsService Class:
public class SmsService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Plug in your sms service here to send a text message. return Task.FromResult(0); } }
تنظیمات Email Service و SMS Service در متد Create، ApplicationUserManager:
برای ایجاد یک نمونه پروژه Identity،تیم Identity یک کلاس helper که در فایل IdentityConfig.cs پیدا می شد اضافه کرده است.
بگذارید نگاهی به کلاس SignInHelper
بیندازیم ما قرار نیست تا در اینجا وارد جزییات شویم .
The Sign-In Helper Class:
public class SignInHelper { public SignInHelper(ApplicationUserManager userManager, IAuthenticationManager authManager) { UserManager = userManager; AuthenticationManager = authManager; } public ApplicationUserManager UserManager { get; private set; } public IAuthenticationManager AuthenticationManager { get; private set; } public async Task SignInAsync(ApplicationUser user, bool isPersistent, bool rememberBrowser) { // Clear any partial cookies from external or two factor partial sign ins AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.TwoFactorCookie); var userIdentity = await user.GenerateUserIdentityAsync(UserManager); if (rememberBrowser) { var rememberBrowserIdentity = AuthenticationManager.CreateTwoFactorRememberBrowserIdentity(user.Id); AuthenticationManager.SignIn( new AuthenticationProperties { IsPersistent = isPersistent }, userIdentity, rememberBrowserIdentity); } else { AuthenticationManager.SignIn( new AuthenticationProperties { IsPersistent = isPersistent }, userIdentity); } } public async Task<bool> SendTwoFactorCode(string provider) { var userId = await GetVerifiedUserIdAsync(); if (userId == null) { return false; } var token = await UserManager.GenerateTwoFactorTokenAsync(userId, provider); // See IdentityConfig.cs to plug in Email/SMS services to actually send the code await UserManager.NotifyTwoFactorTokenAsync(userId, provider, token); return true; } public async Task<string> GetVerifiedUserIdAsync() { var result = await AuthenticationManager.AuthenticateAsync( DefaultAuthenticationTypes.TwoFactorCookie); if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserId())) { return result.Identity.GetUserId(); } return null; } public async Task<bool> HasBeenVerified() { return await GetVerifiedUserIdAsync() != null; } public async Task<SignInStatus> TwoFactorSignIn( string provider, string code, bool isPersistent, bool rememberBrowser) { var userId = await GetVerifiedUserIdAsync(); if (userId == null) { return SignInStatus.Failure; } var user = await UserManager.FindByIdAsync(userId); if (user == null) { return SignInStatus.Failure; } if (await UserManager.IsLockedOutAsync(user.Id)) { return SignInStatus.LockedOut; } if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, provider, code)) { // When token is verified correctly, clear the access failed // count used for lockout await UserManager.ResetAccessFailedCountAsync(user.Id); await SignInAsync(user, isPersistent, rememberBrowser); return SignInStatus.Success; } // If the token is incorrect, record the failure which // also may cause the user to be locked out await UserManager.AccessFailedAsync(user.Id); return SignInStatus.Failure; } public async Task<SignInStatus> ExternalSignIn( ExternalLoginInfo loginInfo, bool isPersistent) { var user = await UserManager.FindAsync(loginInfo.Login); if (user == null) { return SignInStatus.Failure; } if (await UserManager.IsLockedOutAsync(user.Id)) { return SignInStatus.LockedOut; } return await SignInOrTwoFactor(user, isPersistent); } private async Task<SignInStatus> SignInOrTwoFactor(ApplicationUser user, bool isPersistent) { if (await UserManager.GetTwoFactorEnabledAsync(user.Id) && !await AuthenticationManager.TwoFactorBrowserRememberedAsync(user.Id)) { var identity = new ClaimsIdentity(DefaultAuthenticationTypes.TwoFactorCookie); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); AuthenticationManager.SignIn(identity); return SignInStatus.RequiresTwoFactorAuthentication; } await SignInAsync(user, isPersistent, false); return SignInStatus.Success; } public async Task<SignInStatus> PasswordSignIn( string userName, string password, bool isPersistent, bool shouldLockout) { var user = await UserManager.FindByNameAsync(userName); if (user == null) { return SignInStatus.Failure; } if (await UserManager.IsLockedOutAsync(user.Id)) { return SignInStatus.LockedOut; } if (await UserManager.CheckPasswordAsync(user, password)) { return await SignInOrTwoFactor(user, isPersistent); } if (shouldLockout) { // If lockout is requested, increment access failed // count which might lock out the user await UserManager.AccessFailedAsync(user.Id); if (await UserManager.IsLockedOutAsync(user.Id)) { return SignInStatus.LockedOut; } } return SignInStatus.Failure; } }
متدهای قابل دسترس در کلاس SignInHelper
بعضی از مولفه های جدید را در Identity 2.0 معرفی می کند.ما در آن متد اشنای SignInAsync()
را می بینیم.
ما می توانیم به متد Login در AccountController
به این دید که چگونه اجازه دسترسی در Identity 2.0 مدیریت می شود نگاهی بیندازیم .
متد Login در AccountController
در با استفاده از Identity 2.0:
[AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (!ModelState.IsValid) { return View(model); } // This doesn't count login failures towards lockout only two factor authentication // To enable password failures to trigger lockout, change to shouldLockout: true var result = await SignInHelper.PasswordSignIn(model.Email, model.Password, model.RememberMe, shouldLockout: false); switch (result) { case SignInStatus.Success: return RedirectToLocal(returnUrl); case SignInStatus.LockedOut: return View("Lockout"); case SignInStatus.RequiresTwoFactorAuthentication: return RedirectToAction("SendCode", new { ReturnUrl = returnUrl }); case SignInStatus.Failure: default: ModelState.AddModelError("", "Invalid login attempt."); return View(model); } }
اگر ما نگاه سریعی به این بیندازیم که چگونه عملیات log-in در پروژه MVC با استفاده از Identity 1.0 مدیریت می شود .ما مستقیما باید به متد AccountController.Login
برویم و کدهای زیر را می بینیم.
متد Login در AccountController
در با استفاده از Identity 1.0:
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (ModelState.IsValid) { var user = await UserManager.FindAsync(model.UserName, model.Password); if (user != null) { await SignInAsync(user, model.RememberMe); return RedirectToLocal(returnUrl); } else { ModelState.AddModelError("", "Invalid username or password."); } } // If we got this far, something failed, redisplay form return View(model); }
طبق متد بالا به کلاس UserManager
شبیه به کدی که در SignInHelper
دیدیم می رویم.ما همچنین متد SignInAsync
را که مستقیما در AccountController
تعریف شده بود فراخوانی می کنیم.
متد SignInAsync در AccountController
در با استفاده از Identity 1.0:
private async Task SignInAsync(ApplicationUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync( user, DefaultAuthenticationTypes.ApplicationCookie); AuthenticationManager.SignIn( new AuthenticationProperties() { IsPersistent = isPersistent }, identity); }
ApplicationDbContext:
اگر شما با ASP.NET MVC کار کرده باشید حتما با ApplicationDbContext
اشنایی دارید میدانید که
آن یک کلاس اجرایی پیش فرض Entity Framework است که application شما، Identity-related data. را دسترسی دارد و ذخیره می کند.
در این پروژه تیم کمی متفاوت با استاندارد های در یک پروژه ASP با استفاده از Identity 1.0 رفتار کرده است .در این مثال ما نگاهی به فایل IdentityModels.cs می اندازیم و در نتیجه کلاس تعریف شده ApplicationDbContext
را پیدا می کنیم.
کلاس ApplicationDbContext در پروژه مثال Identity 2.0:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false) { } static ApplicationDbContext() { // Set the database intializer which is run once during application start // This seeds the database with admin user credentials and admin role Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer()); } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } }
کد بالا دو متد استاتیک Create()
و ApplicationDbContext()
را ست می کند که این دو دیتابیس را مقداردهی می کند،دومین متد چک می کند که مقدار دهی دیتابیس در کلاس ApplicationDbInitializer
قرار داده شده است.
کلاس ApplicationDbInitializer در فایل IdentityConfig.cs:
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext> { protected override void Seed(ApplicationDbContext context) { InitializeIdentityForEF(context); base.Seed(context); } public static void InitializeIdentityForEF(ApplicationDbContext db) { var userManager = HttpContext .Current.GetOwinContext() .GetUserManager<ApplicationUserManager>(); var roleManager = HttpContext.Current .GetOwinContext() .Get<ApplicationRoleManager>(); const string name = "admin@admin.com"; const string password = "Admin@123456"; const string roleName = "Admin"; //Create Role Admin if it does not exist var role = roleManager.FindByName(roleName); if (role == null) { role = new IdentityRole(roleName); var roleresult = roleManager.Create(role); } var user = userManager.FindByName(name); if (user == null) { user = new ApplicationUser { UserName = name, Email = name }; var result = userManager.Create(user, password); result = userManager.SetLockoutEnabled(user.Id, false); } // Add user admin to Role Admin if not already added var rolesForUser = userManager.GetRoles(user.Id); if (!rolesForUser.Contains(role.Name)) { var result = userManager.AddToRole(user.Id, role.Name); } } }
نتیجه گیری:
در این مقاله ما نگاهی به بعضی از مولفه های و پیکر بندی ها در پروژه ASP.NET MVC با استفاده از Identity 2.0 framework انداختیم اما هنوز مسایل بسیاری وجود دارد که ما به ان نپرداختیم.
- ASP.net MVC
- 6k بازدید
- 24 تشکر