راهنمای تغییر بخش احراز هویت و اعتبارسنجی کاربران سیستم مدیریت محتوای IRIS به ASP.NET Identity – بخش سوم
نویسنده: مهدی سعیدی فر
تاریخ: ۱۳۹۴/۰۸/۲۸ ۲۳:۱۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public class IrisPasswordHasher : IPasswordHasher
{
public string HashPassword(string password)
{
return Utilities.Security.Encryption.EncryptingPassword(password);
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
return Utilities.Security.Encryption.VerifyPassword(providedPassword, hashedPassword) ?
PasswordVerificationResult.Success :
PasswordVerificationResult.Failed;
}
} public ApplicationUserManager(IUserStore<ApplicationUser, int> store,
IUnitOfWork uow,
IIdentity identity,
IApplicationRoleManager roleManager,
IDataProtectionProvider dataProtectionProvider,
IIdentityMessageService smsService,
IIdentityMessageService emailService, IPasswordHasher passwordHasher)
: base(store)
{
_store = store;
_uow = uow;
_identity = identity;
_users = _uow.Set<ApplicationUser>();
_roleManager = roleManager;
_dataProtectionProvider = dataProtectionProvider;
this.SmsService = smsService;
this.EmailService = emailService;
PasswordHasher = passwordHasher;
createApplicationUserManager();
} x.For<IPasswordHasher>().Use<IrisPasswordHasher>();
[HttpPost]
[ValidateAntiForgeryToken]
[CaptchaVerify("تصویر امنیتی وارد شده معتبر نیست")]
public virtual async Task<ActionResult> Register(RegisterModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
CreatedDate = DateAndTime.GetDateTime(),
Email = model.Email,
IP = Request.ServerVariables["REMOTE_ADDR"],
IsBaned = false,
UserName = model.UserName,
UserMetaData = new UserMetaData(),
LastLoginDate = DateAndTime.GetDateTime()
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var addToRoleResult = await _userManager.AddToRoleAsync(user.Id, "user");
if (addToRoleResult.Succeeded)
{
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "User",
new { userId = user.Id, code }, protocol: Request.Url.Scheme);
_emailService.SendAccountConfirmationEmail(user.Email, callbackUrl);
return Json(new { result = "success" });
}
addErrors(addToRoleResult);
}
addErrors(result);
}
return PartialView(MVC.User.Views._Register, model);
} public SendingMailResult SendAccountConfirmationEmail(string email, string link)
{
var model = new ConfirmEmailModel()
{
ActivationLink = link
};
var htmlText = _viewConvertor.RenderRazorViewToString(MVC.EmailTemplates.Views._ConfirmEmail, model);
var result = Send(new MailDocument
{
Body = htmlText,
Subject = "تایید حساب کاربری",
ToEmail = email
});
return result;
} @model Iris.Model.EmailModel.ConfirmEmailModel
<div style="direction: rtl; -ms-word-wrap: break-word; word-wrap: break-word;">
<p>با سلام</p>
<p>برای فعال سازی حساب کاربری خود لطفا بر روی لینک زیر کلیک کنید:</p>
<p>@Model.ActivationLink</p>
<div style=" color: #808080;">
<p>با تشکر</p>
<p>@Model.SiteTitle</p>
<p>@Model.SiteDescription</p>
<p><span style="direction: ltr !important; unicode-bidi: embed;">@Html.ConvertToPersianDateTime(DateTime.Now, "s,H")</span></p>
</div>
</div> RegisterUser.Form.onSuccess = function (data) {
if (data.result == "success") {
var message = '<div id="alert"><button type="button" data-dismiss="alert">×</button>ایمیلی حاوی لینک فعال سازی، به ایمیل شما ارسال شد؛ لطفا به ایمیل خود مراجعه کرده و بر روی لینک فعال سازی کلیک کنید.</div>';
$('#registerResult').html(message);
}
else {
$('#logOnModal').html(data);
}
}; [AllowAnonymous]
public virtual async Task<ActionResult> ConfirmEmail(int? userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await _userManager.ConfirmEmailAsync(userId.Value, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
} @{
ViewBag.Title = "حساب کاربری شما تایید شد";
}
<h2>@ViewBag.Title.</h2>
<div>
<p>
با تشکر از شما، حساب کاربری شما تایید شد.
</p>
<p>
@Ajax.ActionLink("ورود / ثبت نام", MVC.User.ActionNames.LogOn, MVC.User.Name, new { area = "", returnUrl = Html.ReturnUrl(Context, Url) }, new AjaxOptions { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "logOnModal", LoadingElementDuration = 300, LoadingElementId = "loadingMessage", OnSuccess = "LogOnForm.onSuccess" }, new { role = "button", data_toggle = "modal", data_i_logon_link = "true", rel = "nofollow" })
</p>
</div> [HttpPost]
[ValidateAntiForgeryToken]
public async virtual Task<ActionResult> LogOn(LogOnModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
if (Request.IsAjaxRequest())
return PartialView(MVC.User.Views._LogOn, model);
return View(model);
}
const string emailRegPattern =
@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
string ip = Request.ServerVariables["REMOTE_ADDR"];
SignInStatus result = SignInStatus.Failure;
if (Regex.IsMatch(model.Identity, emailRegPattern))
{
var user = await _userManager.FindByEmailAsync(model.Identity);
if (user != null)
{
result = await _signInManager.PasswordSignInAsync
(user.UserName,
model.Password, model.RememberMe, shouldLockout: true);
}
}
else
{
result = await _signInManager.PasswordSignInAsync(model.Identity, model.Password, model.RememberMe, shouldLockout: true);
}
switch (result)
{
case SignInStatus.Success:
if (Request.IsAjaxRequest())
return JavaScript(IsValidReturnUrl(returnUrl)
? string.Format("window.location ='{0}';", returnUrl)
: "window.location.reload();");
return redirectToLocal(returnUrl);
case SignInStatus.LockedOut:
ModelState.AddModelError("",
string.Format("حساب شما قفل شد، لطفا بعد از {0} دقیقه دوباره امتحان کنید.",
_userManager.DefaultAccountLockoutTimeSpan.Minutes));
break;
case SignInStatus.Failure:
ModelState.AddModelError("", "نام کاربری یا کلمه عبور اشتباه است.");
break;
default:
ModelState.AddModelError("", "در ورود شما خطایی رخ داده است.");
break;
}
if (Request.IsAjaxRequest())
return PartialView(MVC.User.Views._LogOn, model);
return View(model);
} [HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public virtual ActionResult LogOut()
{
_authenticationManager.SignOut();
if (Request.IsAjaxRequest())
return Json(new { result = "true" });
return RedirectToAction(MVC.User.ActionNames.LogOn, MVC.User.Name);
} using System.Threading.Tasks;
using System.Web.Mvc;
using CaptchaMvc.Attributes;
using Iris.Model;
using Iris.Servicelayer.Interfaces;
using Iris.Web.Email;
using Microsoft.AspNet.Identity;
namespace Iris.Web.Controllers
{
public partial class ForgottenPasswordController : Controller
{
private readonly IEmailService _emailService;
private readonly IApplicationUserManager _userManager;
public ForgottenPasswordController(IEmailService emailService, IApplicationUserManager applicationUserManager)
{
_emailService = emailService;
_userManager = applicationUserManager;
}
[HttpGet]
public virtual ActionResult Index()
{
return PartialView(MVC.ForgottenPassword.Views._Index);
}
[HttpPost]
[ValidateAntiForgeryToken]
[CaptchaVerify("تصویر امنیتی وارد شده معتبر نیست")]
public async virtual Task<ActionResult> Index(ForgottenPasswordModel model)
{
if (!ModelState.IsValid)
{
return PartialView(MVC.ForgottenPassword.Views._Index, model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user.Id)))
{
// Don't reveal that the user does not exist or is not confirmed
return Json(new
{
result = "false",
message = "این ایمیل در سیستم ثبت نشده است"
});
}
var code = await _userManager.GeneratePasswordResetTokenAsync(user.Id);
_emailService.SendResetPasswordConfirmationEmail(user.UserName, user.Email, code);
return Json(new
{
result = "true",
message = "ایمیلی برای تایید بازنشانی کلمه عبور برای شما ارسال شد.اعتبارایمیل ارسالی 3 ساعت است."
});
}
[AllowAnonymous]
public virtual ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
[AllowAnonymous]
public virtual ActionResult ResetPasswordConfirmation()
{
return View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public virtual async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("Error");
}
var result = await _userManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "ForgottenPassword");
}
addErrors(result);
return View();
}
private void addErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
}
} @{
ViewBag.Title = "کلمه عبور شما تغییر کرد";
}
<hgroup>
<h1>@ViewBag.Title.</h1>
</hgroup>
<div>
<p>
کلمه عبور شما با موفقیت تغییر کرد
</p>
<p>
@Ajax.ActionLink("ورود / ثبت نام", MVC.User.ActionNames.LogOn, MVC.User.Name, new { area = "", returnUrl = Html.ReturnUrl(Context, Url) }, new AjaxOptions { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "logOnModal", LoadingElementDuration = 300, LoadingElementId = "loadingMessage", OnSuccess = "LogOnForm.onSuccess" }, new { role = "button", data_toggle = "modal", data_i_logon_link = "true", rel = "nofollow" })
</p>
</div> @model Iris.Model.ResetPasswordViewModel
@{
ViewBag.Title = "ریست کردن کلمه عبور";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("ResetPassword", "ForgottenPassword", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>ریست کردن کلمه عبور</h4>
<hr />
@Html.ValidationSummary("", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Code)
<div>
@Html.LabelFor(m => m.Email, "ایمیل", new { @class = "control-label" })
<div>
@Html.TextBoxFor(m => m.Email)
</div>
</div>
<div>
@Html.LabelFor(m => m.Password, "کلمه عبور", new { @class = "control-label" })
<div>
@Html.PasswordFor(m => m.Password)
</div>
</div>
<div>
@Html.LabelFor(m => m.ConfirmPassword, "تکرار کلمه عبور", new { @class = "control-label" })
<div>
@Html.PasswordFor(m => m.ConfirmPassword)
</div>
</div>
<div>
<div>
<input type="submit" value="تغییر کلمه عبور" />
</div>
</div>
} using System.ComponentModel.DataAnnotations;
namespace Iris.Model
{
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "ایمیل")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "کلمه عبور باید حداقل 6 حرف باشد", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "کلمه عبور")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "تکرار کلمه عبور")]
[Compare("Password", ErrorMessage = "کلمه عبور و تکرارش یکسان نیستند")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
} var principalService = ObjectFactory.GetInstance<IPrincipalService>(); var formsAuthenticationService = ObjectFactory.GetInstance<IFormsAuthenticationService>(); context.User = principalService.GetCurrent()
public class CustomUserValidator<TUser, TKey> : IIdentityValidator<ApplicationUser>
where TUser : class, IUser<int>
where TKey : IEquatable<int>
{
public bool AllowOnlyAlphanumericUserNames { get; set; }
public bool RequireUniqueEmail { get; set; }
private ApplicationUserManager Manager { get; set; }
public CustomUserValidator(ApplicationUserManager manager)
{
if (manager == null)
throw new ArgumentNullException("manager");
AllowOnlyAlphanumericUserNames = true;
Manager = manager;
}
public virtual async Task<IdentityResult> ValidateAsync(ApplicationUser item)
{
if (item == null)
throw new ArgumentNullException("item");
var errors = new List<string>();
await ValidateUserName(item, errors);
if (RequireUniqueEmail)
await ValidateEmailAsync(item, errors);
return errors.Count <= 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
}
private async Task ValidateUserName(ApplicationUser user, ICollection<string> errors)
{
if (string.IsNullOrWhiteSpace(user.UserName))
errors.Add("نام کاربری نباید خالی باشد");
else if (AllowOnlyAlphanumericUserNames && !Regex.IsMatch(user.UserName, "^[A-Za-z0-9@_\\.]+$"))
{
errors.Add("برای نام کاربری فقط از کاراکترهای مجاز استفاده کنید ");
}
else
{
var owner = await Manager.FindByNameAsync(user.UserName);
if (owner != null && !EqualityComparer<int>.Default.Equals(owner.Id, user.Id))
errors.Add("این نام کاربری قبلا ثبت شده است");
}
}
private async Task ValidateEmailAsync(ApplicationUser user, ICollection<string> errors)
{
var email = await Manager.GetEmailStore().GetEmailAsync(user).WithCurrentCulture();
if (string.IsNullOrWhiteSpace(email))
{
errors.Add("وارد کردن ایمیل ضروریست");
}
else
{
try
{
var m = new MailAddress(email);
}
catch (FormatException)
{
errors.Add("ایمیل را به شکل صحیح وارد کنید");
return;
}
var owner = await Manager.FindByEmailAsync(email);
if (owner != null && !EqualityComparer<int>.Default.Equals(owner.Id, user.Id))
errors.Add("این ایمیل قبلا ثبت شده است");
}
}
} public class CustomPasswordValidator : IIdentityValidator<string>
{
#region Properties
public int RequiredLength { get; set; }
public bool RequireNonLetterOrDigit { get; set; }
public bool RequireLowercase { get; set; }
public bool RequireUppercase { get; set; }
public bool RequireDigit { get; set; }
#endregion
#region IIdentityValidator
public virtual Task<IdentityResult> ValidateAsync(string item)
{
if (item == null)
throw new ArgumentNullException("item");
var list = new List<string>();
if (string.IsNullOrWhiteSpace(item) || item.Length < RequiredLength)
list.Add(string.Format("کلمه عبور نباید کمتر از 6 کاراکتر باشد"));
if (RequireNonLetterOrDigit && item.All(IsLetterOrDigit))
list.Add("برای امنیت بیشتر از حداقل از یک کارکتر غیر عددی و غیر حرف برای کلمه عبور استفاده کنید");
if (RequireDigit && item.All(c => !IsDigit(c)))
list.Add("برای امنیت بیشتر از اعداد هم در کلمه عبور استفاده کنید");
if (RequireLowercase && item.All(c => !IsLower(c)))
list.Add("از حروف کوچک نیز برای کلمه عبور استفاده کنید");
if (RequireUppercase && item.All(c => !IsUpper(c)))
list.Add("از حروف بزرک نیز برای کلمه عبور استفاده کنید");
return Task.FromResult(list.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(string.Join(" ", list)));
}
#endregion
#region PrivateMethods
public virtual bool IsDigit(char c)
{
if (c >= 48)
return c <= 57;
return false;
}
public virtual bool IsLower(char c)
{
if (c >= 97)
return c <= 122;
return false;
}
public virtual bool IsUpper(char c)
{
if (c >= 65)
return c <= 90;
return false;
}
public virtual bool IsLetterOrDigit(char c)
{
if (!IsUpper(c) && !IsLower(c))
return IsDigit(c);
return true;
}
#endregion
} UserValidator = new CustomUserValidator< ApplicationUser, int>(this)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
PasswordValidator = new CustomPasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false
}; public virtual async Task<ActionResult> UpdateAllUsersSecurityStamp()
{
foreach (var user in await _userManager.GetAllUsersAsync())
{
await _userManager.UpdateSecurityStampAsync(user.Id);
}
return Content("ok");
} public virtual async Task<ActionResult> CopyRoleToNewTable()
{
var dbContext = new IrisDbContext();
foreach (var role in await dbContext.Roles.ToListAsync())
{
await _roleManager.CreateAsync(new CustomRole(role.Name)
{
Description = role.Description
});
}
var users = await dbContext.Users.Include(u => u.Role).ToListAsync();
foreach (var user in users)
{
await _userManager.AddToRoleAsync(user.Id, user.Role.Name);
}
return Content("ok");
}