آموزش سفارشی کردن Asp.Net Identity با پایگاه داده خارجی

سه شنبه 4 آبان 1395

Asp.Net Identity به این دلیل طراحی شده است تا به ما توانایی و قابلیت استفاده از Storage Providerهای مختلف را در برنامه های ASP.Net ، به ما بدهد . ما میتوانیم ازIdentity Providerهایی که با Net Framework. عرضه شده است استفاده کنیم و هم میتوانیم Providerهای خودمان را طراحی کنیم . در این مقاله به بررسی ایجاد Provider های سفارشی خواهیم پرداخت .

آموزش سفارشی کردن Asp.Net Identity با پایگاه داده خارجی

آموزش تصویری نصب و استفاده از Asp.Net Identity 2

دو دلیل اساسی برای ایجاد Providerهای سفارشی وجود دارد . 

ما نیاز به ذخیره‌سازی اطلاعات Identity در data sourceای راداریم که توسط Identity Provider ای که در 
Net Framework. وجود دارد ، پشتیبانی نمی‌شود ، مثل پایگاه داده های MySQL ، یک پایگاه داده Oracle یا منابع داده های دیگر . 
ما باید اطلاعات Identity را با استفاده از یک Database schema ای که با Provider ، Database schema ما متفاوت است ، مدیریت کنیم . 

در مثالی که در ادامه مقاله مشاهده خواهید کرد ، ما قصد پیاده سازی و پیکربندی یک Identity Provider سفارشی با استفاده از ASP.Net MVC 5 را داریم .

ایجاد پروژه ASP.Net MVC :

Visual Studio راباز کرده و یک پروژه ASP.NET Web application ایجاد کنید و MVC Template را انتخاب کنید . 



بعد از ایجاد شدن پروژه F5 را میزنیم تا برنامه اجرا شود . بعد اجرا Register را برای ساختن یک کاربر جدید میزنیم . مراحل زیر را دنبال کنید :




بعد از پر کردن فیلد های مربوط به ثبت نام Register را بزنید :



در Solution Explorer روی فولدر App_Data کلیک راست کرده و فولدر root آن را باز کنید :





خواهید دید که بعد از اجرای برنامه پایگاه داده ایی با نام aspnet-AspNetIdentity-20160402093629 ساخته خواهد شد ، asp.net از این پایگاه داده برای ذخیره سازی اطلاعات امنیتی نظیر username ، Password و ... استفاده خواهد کرد .

توجه داشته باشید که aspnet-AspNetIdentity-2016040209362 در پایگاه داده ساخته نشده است اما به آن ضمیمه شده است . پس ، ما پایگاه داده را به SQL Server منتقل میکنیم و آن برای Customize Identity استفاده می‌کنیم . 

در Visual Studio در قسمت منو، روی View کلیک و سپس روی SQL Server Object Explorer کلیک کنید ، پایگاه داده خود را انتخاب کرده و فولدر Table را باز کنید :

پنج Table ساخته شده است :
AspNetRoles, AspNetUserClaims, AspNetUserLogins, AspNetUserRoles, AspNetUsers .



با ایجاد یک database diagram ما میتوانیم یک نگاه اجمالی به modelهای پایگاه داده خود داشته باشیم :


در نهایت ، فایل web.Config را باز کرده و مشخصات  ConnectionString را برای پایگاه داده خود تنظیم کنید :
حال F5 را میزنیم تا برنامه اجرا شود ، بعد به سراغ ایجاد یک کاربر جدید می‌رویم :

درحال حاضر برنامه ما، برای استفاده از یک Storage خارجی آماده است . در مرحله بعد ، چگونگی سفارشی کردن پایگاه داده خارجی را برای شما بیان خواهیم کرد .

ایجاد Identity Library Project :

یک class library ایجاد کرده و نام آن را IdentityLibrary میگذاریم :
روی References کلیک راست کرده و Manage NuGet Packages را انتخاب کنید :



Microsoft.AspNet.Identity.Core و Microsoft.AspNet.Identity.EntityFramework را نصب کنید .



یک کلاس با نام IdentityUser  برای نگهداری اطلاعات کاربران ایجاد کنید ، این کلاس از IdentityUser ارث‌بری می‌کند :

 


کلاس IdentityRole را برای نگهداری اطلاعات در مورد نقش کاربران را ایجاد کنید :




یک کلاس با نام UserStore ایجاد کنید ، Asp.Net MVC بصورت پیش فرض از این کلاس استفاده خواهد کرد :

var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));

اما با این وجود ، ما برای سفارشی کردن Storage Provider آن را پیاده سازی میکنیم :


برای پیاده سازی IRoleIdentity ، کلاسی با نام RoleStore می‌کنیم :


ایجاد یک Entity Framework dataModel :

روی پروژه IdentityLibrary راست کلیک کرده و add new item را بزنید ،  ADO.NET Entity Data Modeرا انتخاب کرده و Add را بزنید :


Code First From Database را انتخاب کرده و Next را بزنید :





اگه ما یک data connection داریم می‌توانیم آن را انتخاب کنیم یا اینکه یک connection جدید ایجاد کنیم :




Server Name را انتخاب کنید ، از Windows Authentication یا SQL Server Authentication استفاده کنید ،
سپس پایگاه داده مورد نظر را انتخاب کرده و Ok را بزنید :



جداول پایگاه داده را چک انتخاب کرده تا model ما را تولید کند :



خب ، پنج کلاس در پروژه IdentityLibrary ایجاد شده است ، که به جداول پایگاه داده متصل است :



برای هدف قرار دادن ASP.Net Client ،  Custom Identity Library را پیکربندی میکنیم :

  




پیاده سازی Custom Identity :

• پیاده سازی UserStore :

/// <summary>
/// Class that implements the key ASP.NET Identity user store iterfaces
/// </summary>
public class UserStore<T> : IUserRoleStore<T>,
IUserStore<T>,
IUserPasswordStore<T>,
IUserEmailStore<T>,
IUserLockoutStore<T, string>,
IUserTwoFactorStore<T, string>
where T : IdentityUser
{
private readonly UserRepository<T> _userTable;
private readonly UserRolesRepository _userRolesTable;

public UserStore(DatabaseContext databaseContext)
{
_userTable = new UserRepository<T>(databaseContext);
_userRolesTable = new UserRolesRepository(databaseContext);
}

public Task CreateAsync(T user)
{
if (user == null)
{
throw new ArgumentNullException(“user”);
}
return Task.Run(() => _userTable.Insert(user));
}

public Task<T> FindByIdAsync(string userId)
{
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException(“Null or empty argument: userId”);
}

return Task.Run(() => _userTable.GeTById(userId));
}

public Task<bool> GetTwoFactorEnabledAsync(T user)
{
return Task.FromResult(user.TwoFactorEnabled);
}

public Task<T> FindByNameAsync(string userName)
{
if (string.IsNullOrEmpty(userName))
{
throw new ArgumentException(“Null or empty argument: userName”);
}

return Task.Run(() => _userTable.GeTByName(userName));
}

public Task<IList<string>> GetRolesAsync(T user)
{
if (user == null)
{
throw new ArgumentNullException(“user”);
}

return Task.Run(() => _userRolesTable.FindByUserId(user.Id));
}

public Task<string> GetPasswordHashAsync(T user)
{
return Task.Run(() => _userTable.GetPasswordHash(user.Id));
}

public Task SetPasswordHashAsync(T user, string passwordHash)
{
return Task.Run(() => user.PasswordHash = passwordHash);
}

public Task<T> FindByEmailAsync(string email)
{
if (String.IsNullOrEmpty(email))
{
throw new ArgumentNullException(“email”);
}

return Task.Run(() => _userTable.GeTByEmail(email));
}

public Task<string> GetEmailAsync(T user)
{
return Task.FromResult(user.Email);
}

public Task<int> GetAccessFailedCountAsync(T user)
{
return Task.FromResult(user.AccessFailedCount);
}

public Task<bool> GetLockoutEnabledAsync(T user)
{
return Task.FromResult(user.LockoutEnabled);
}

public Task<DateTimeOffset> GetLockoutEndDateAsync(T user)
{
return
Task.FromResult(user.LockoutEndDateUtc.HasValue
? new DateTimeOffset(DateTime.SpecifyKind(user.LockoutEndDateUtc.Value, DateTimeKind.Utc))
: new DateTimeOffset());
}

public Task SetLockoutEnabledAsync(T user, bool enabled)
{
user.LockoutEnabled = enabled;

return Task.Run(() => _userTable.Update(user));
}

public Task SetLockoutEndDateAsync(T user, DateTimeOffset lockoutEnd)
{
throw new NotImplementedException();
}

public Task SetTwoFactorEnabledAsync(T user, bool enabled)
{
throw new NotImplementedException();
}

public Task DeleteAsync(T user)
{
throw new NotImplementedException();
}

public Task<int> IncrementAccessFailedCountAsync(T user)
{
throw new NotImplementedException();
}

public Task ResetAccessFailedCountAsync(T user)
{
throw new NotImplementedException();
}

public Task<bool> GetEmailConfirmedAsync(T user)
{
throw new NotImplementedException();
}

public Task SetEmailAsync(T user, string email)
{
throw new NotImplementedException();
}

public Task SetEmailConfirmedAsync(T user, bool confirmed)
{
throw new NotImplementedException();
}

public Task<bool> IsInRoleAsync(T user, string roleName)
{
throw new NotImplementedException();
}

public Task RemoveFromRoleAsync(T user, string roleName)
{
throw new NotImplementedException();
}

public Task<bool> HasPasswordAsync(T user)
{
throw new NotImplementedException();
}

public Task UpdateAsync(T user)
{
throw new NotImplementedException();
}

public Task AddToRoleAsync(T user, string roleName)
{
throw new NotImplementedException();
}

public void Dispose()
{
//throw new NotImplementedException();
}
}

• ایجاد یک کلاس UserRepository و پیاده‌سازی آن :



public class UserRepository<T> where T : IdentityUser
{
private readonly DatabaseContext _databaseContext;

public UserRepository(DatabaseContext databaseContext)
{
_databaseContext = databaseContext;
}

internal T GeTByName(string userName)
{
var user = _databaseContext.AspNetUsers.SingleOrDefault(u => u.UserName == userName);
if (user != null)
{
T result = (T)Activator.CreateInstance(typeof(T));
result.Id = user.Id;
result.UserName = user.UserName;
result.PasswordHash = user.PasswordHash;
result.SecurityStamp = user.SecurityStamp;
result.Email = result.Email;
result.EmailConfirmed = user.EmailConfirmed;
result.PhoneNumber = user.PhoneNumber;
result.PhoneNumberConfirmed = user.PhoneNumberConfirmed;
result.LockoutEnabled = user.LockoutEnabled;
result.LockoutEndDateUtc = user.LockoutEndDateUtc;
result.AccessFailedCount = user.AccessFailedCount;
return result;
}
return null;
}

internal T GeTByEmail(string email)
{
var user = _databaseContext.AspNetUsers.SingleOrDefault(u => u.Email == email);
if (user != null)
{
T result = (T)Activator.CreateInstance(typeof(T));

result.Id = user.Id;
result.UserName = user.UserName;
result.PasswordHash = user.PasswordHash;
result.SecurityStamp = user.SecurityStamp;
result.Email = result.Email;
result.EmailConfirmed = user.EmailConfirmed;
result.PhoneNumber = user.PhoneNumber;
result.PhoneNumberConfirmed = user.PhoneNumberConfirmed;
result.LockoutEnabled = user.LockoutEnabled;
result.LockoutEndDateUtc = user.LockoutEndDateUtc;
result.AccessFailedCount = user.AccessFailedCount;
return result;
}
return null;
}

internal int Insert(T user)
{
_databaseContext.AspNetUsers.Add(new AspNetUsers
{
Id = user.Id,
UserName = user.UserName,
PasswordHash = user.PasswordHash,
SecurityStamp = user.SecurityStamp,
Email = user.Email,
EmailConfirmed = user.EmailConfirmed,
PhoneNumber = user.PhoneNumber,
PhoneNumberConfirmed = user.PhoneNumberConfirmed,
LockoutEnabled = user.LockoutEnabled,
LockoutEndDateUtc = user.LockoutEndDateUtc,
AccessFailedCount = user.AccessFailedCount
});

return _databaseContext.SaveChanges();
}

/// <summary>
/// Returns an T given the user’s id
/// </summary>
/// <param name=”userId”>The user’s id</param>
/// <returns></returns>
public T GeTById(string userId)
{
var user = _databaseContext.AspNetUsers.Find(userId);
T result = (T)Activator.CreateInstance(typeof(T));

result.Id = user.Id;
result.UserName = user.UserName;
result.PasswordHash = user.PasswordHash;
result.SecurityStamp = user.SecurityStamp;
result.Email = result.Email;
result.EmailConfirmed = user.EmailConfirmed;
result.PhoneNumber = user.PhoneNumber;
result.PhoneNumberConfirmed = user.PhoneNumberConfirmed;
result.LockoutEnabled = user.LockoutEnabled;
result.LockoutEndDateUtc = user.LockoutEndDateUtc;
result.AccessFailedCount = user.AccessFailedCount;
return result;
}

/// <summary>
/// Return the user’s password hash
/// </summary>
/// <param name=”userId”>The user’s id</param>
/// <returns></returns>
public string GetPasswordHash(string userId)
{
var user = _databaseContext.AspNetUsers.FirstOrDefault(u => u.Id == userId);
var passHash = user != null ? user.PasswordHash : null;
return passHash;
}

/// <summary>
/// Updates a user in the Users table
/// </summary>
/// <param name=”user”></param>
/// <returns></returns>
public int Update(T user)
{
var result = _databaseContext.AspNetUsers.FirstOrDefault(u => u.Id == user.Id);
if (result != null)
{
result.UserName = user.UserName;
result.PasswordHash = user.PasswordHash;
result.SecurityStamp = user.SecurityStamp;
result.Email = result.Email;
result.EmailConfirmed = user.EmailConfirmed;
result.PhoneNumber = user.PhoneNumber;
result.PhoneNumberConfirmed = user.PhoneNumberConfirmed;
result.LockoutEnabled = user.LockoutEnabled;
result.LockoutEndDateUtc = user.LockoutEndDateUtc;
result.AccessFailedCount = user.AccessFailedCount;
return _databaseContext.SaveChanges();
}
return 0;
}
}

 
• ایجاد یک کلاس UserRolesRepository و پیاده سازی آن :



حال ، برنامه ما برای استفاده از IdentityLibrary آماده است .

آموزش asp.net mvc

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید