背景在需要进行表单认证的Asp.NET 5 MVC项目被创建后,往往需要根据项目的实际需求做一系列的工作对MVC 5内建的身份验证机制(Asp.NET Identity)进行扩展和定制:Asp.NET内建的身份验证机制会使用Local DB(本地数据库)读写用户相关的信息,而在数 ...
背景
在需要进行表单认证的Asp.NET 5 MVC项目被创建后,往往需要根据项目的实际需求做一系列的工作对MVC 5内建的身份验证机制(Asp.NET Identity)进行扩展和定制:
- Asp.NET内建的身份验证机制会使用Local DB(本地数据库)读写用户相关的信息,而在数据库驱动的项目中,管理业务信息的数据库通常是特定的数据库环境,比如远程SQL Server数据库实例或Access数据库等等,业务数据库中保存着一系列针对业务需求的数据表,因此需要定制MVC 5内建身份验证,使其操作的的用户表们与业务数据库的表们共处在同一数据库中
- Asp.NET身份验证默认创建的用户表名为:AspNetRoles, AspNetUserClaims, AspNetUserLogins, AspNetUserRoles, AspNetUsers等,与实际业务数据库中自成体系的数据表命名习惯(如tblProduct, PRODUCT, Products...)不一致,因此需要定制MVC 5内建身份验证,使其使用我们指定的表名称保存用户信息,以便与实际业务数据库中的表名称处于相同的命名规范体系
- 实际业务中用户信息往往多于Asp.NET默认提供的,如根据实际情况会需要以用户email登录,或在Users表中保存用户的guid,性别,地址,是否激活等等,因此需要对Asp.net创建的表,以及相应操作的代码进行扩展
总之,一切都是为了减轻管理的负担,提升工作效率,使项目整体变得更加优雅。
要点
本文仅聚焦在表单身份认证(Forms Authentication)的个性化定制
Step 6. 在类库项目中创建Models
6.1 创建Models文件夹
该文件夹用于保存用户验证相关的模型类,这些类都继承自Microsoft.AspNet.Identity.EntityFramework命名空间下相应的类,并显示指定了关键字的类型为long(Asp.NET默认使用
get='_blank'>string类型)
Step 12. 接下来是Controller, 打开MVC项目下Controllers文件夹中的AccountController.cs,修改后的文件如下所示
using System;using System.Threading.Tasks;using System.Web;using System.Web.Mvc;using Core.Models;using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.Owin;using Microsoft.Owin.Security;using MyMvcProject.Models;namespace MyMvcProject.Controllers{ [Authorize] public class AccountController : Controller { #region constants private const string XsrfKey = "XsrfId"; #endregion #region member vars private MyUserManager _userManager; #endregion #region enums public enum ManageMessageId { ChangePasswordSuccess, SetPasswordSuccess, RemoveLoginSuccess, Error } #endregion #region properties public MyUserManager UserManager { get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<MyUserManager>(); } private set { _userManager = value; } } private IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } } #endregion #region constructors and destructors public AccountController() { } public AccountController(MyUserManager userManager) { UserManager = userManager; } protected override void Dispose(bool disposing) { if (disposing && UserManager != null) { UserManager.Dispose(); UserManager = null; } base.Dispose(disposing); } #endregion #region methods [AllowAnonymous] public async Task<ActionResult> ConfirmEmail(long userId, string code) { if (userId == null || code == null) { return View("Error"); } var result = await UserManager.ConfirmEmailAsync(userId, code); if (result.Succeeded) { return View("ConfirmEmail"); } AddErrors(result); return View(); } // // POST: /Account/Disassociate [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Disassociate(string loginProvider, string providerKey) { ManageMessageId? message = null; var result = await UserManager.RemoveLoginAsync(long.Parse(User.Identity.GetUserId()), new UserLoginInfo(loginProvider, providerKey)); if (result.Succeeded) { var user = await UserManager.FindByIdAsync(long.Parse(User.Identity.GetUserId())); await SignInAsync(user, false); message = ManageMessageId.RemoveLoginSuccess; } else { message = ManageMessageId.Error; } return RedirectToAction( "Manage", new { Message = message }); } // // POST: /Account/ExternalLogin [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult ExternalLogin(string provider, string returnUrl) { // Request a redirect to the external login provider return new ChallengeResult( provider, Url.Action( "ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl })); } // // GET: /Account/ExternalLoginCallback [AllowAnonymous] public async Task<ActionResult> ExternalLoginCallback(string returnUrl) { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { return RedirectToAction("Login"); } // Sign in the user with this external login provider if the user already has a login var user = await UserManager.FindAsync(loginInfo.Login); if (user != null) { await SignInAsync(user, false); return RedirectToLocal(returnUrl); } // If the user does not have an account, then prompt the user to create an account ViewBag.ReturnUrl = returnUrl; ViewBag.LoginProvider = loginInfo.Login.LoginProvider; return View( "ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email }); } // // POST: /Account/ExternalLoginConfirmation [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl) { if (User.Identity.IsAuthenticated) { return RedirectToAction("Manage"); } if (ModelState.IsValid) { // Get the information about the user from the external login provider var info = await AuthenticationManager.GetExternalLoginInfoAsync(); if (info == null) { return View("ExternalLoginFailure"); } var user = new MyUser { UserName = model.Email, Email = model.Email }; var result = await UserManager.CreateAsync(user); if (result.Succeeded) { result = await UserManager.AddLoginAsync(user.Id, info.Login); if (result.Succeeded) { await SignInAsync(user, false); // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771 // Send an email with this link // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); // SendEmail(user.Email, callbackUrl, "Confirm your account", "Please confirm your account by clicking this link"); return RedirectToLocal(returnUrl); } } AddErrors(result); } ViewBag.ReturnUrl = returnUrl; return View(model); } // // GET: /Account/ExternalLoginFailure [AllowAnonymous] public ActionResult ExternalLoginFailure() { return View(); } [AllowAnonymous] public ActionResult ForgotPassword() { return View(); } // // POST: /Account/ForgotPassword [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model) { if (ModelState.IsValid) { var user = await UserManager.FindByNameAsync(model.Email); if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id))) { ModelState.AddModelError("", "The user either does not exist or is not confirmed."); return View(); } // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771 // Send an email with this link // string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id); // var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); // await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>"); // return RedirectToAction("ForgotPasswordConfirmation", "Account"); } // If we got this far, something failed, redisplay form return View(model); } // // GET: /Account/ForgotPasswordConfirmation [AllowAnonymous] public ActionResult ForgotPasswordConfirmation() { return View(); } // // POST: /Account/LinkLogin [HttpPost] [ValidateAntiForgeryToken] public ActionResult LinkLogin(string provider) { // Request a redirect to the external login provider to link a login for the current user return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId()); } // // GET: /Account/LinkLoginCallback public async Task<ActionResult> LinkLoginCallback() { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId()); if (loginInfo == null) { return RedirectToAction( "Manage", new { Message = ManageMessageId.Error }); } var result = await UserManager.AddLoginAsync(long.Parse(User.Identity.GetUserId()), loginInfo.Login); if (result.Succeeded) { return RedirectToAction("Manage"); } return RedirectToAction( "Manage", new { Message = ManageMessageId.Error }); } // // POST: /Account/LogOff [HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { AuthenticationManager.SignOut(); return RedirectToAction("Index", "Home"); } // // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } // // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (ModelState.IsValid) { var user = await UserManager.FindAsync(model.Email, model.Password); if (user != null) { await SignInAsync(user, model.RememberMe); return RedirectToLocal(returnUrl); } ModelState.AddModelError("", "Invalid username or password."); } // If we got this far, something failed, redisplay form return View(model); } // // GET: /Account/Manage public ActionResult Manage(ManageMessageId? message) { ViewBag.StatusMessage = message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." : message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." : message == ManageMessageId.Error ? "An error has occurred." : ""; ViewBag.HasLocalPassword = HasPassword(); ViewBag.ReturnUrl = Url.Action("Manage"); return View(); } // // POST: /Account/Manage [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Manage(ManageUserViewModel model) { var hasPassword = HasPassword(); ViewBag.HasLocalPassword = hasPassword; ViewBag.ReturnUrl = Url.Action("Manage"); if (hasPassword) { if (ModelState.IsValid) { var result = await UserManager.ChangePasswordAsync(long.Parse(User.Identity.GetUserId()), model.OldPassword, model.NewPassword); if (result.Succeeded) { var user = await UserManager.FindByIdAsync(long.Parse(User.Identity.GetUserId())); await SignInAsync(user, false); return RedirectToAction( "Manage", new { Message = ManageMessageId.ChangePasswordSuccess }); } AddErrors(result); } } else { // User does not have a password so remove any validation errors caused by a missing OldPassword field var state = ModelState["OldPassword"]; if (state != null) { state.Errors.Clear(); } if (ModelState.IsValid) { var result = await UserManager.AddPasswordAsync(long.Parse(User.Identity.GetUserId()), model.NewPassword); if (result.Succeeded) { return RedirectToAction( "Manage", new { Message = ManageMessageId.SetPasswordSuccess }); } AddErrors(result); } } // If we got this far, something failed, redisplay form return View(model); } // // GET: /Account/Register [AllowAnonymous] public ActionResult Register() { return View(); } // // POST: /Account/Register [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new MyUser { UserName = model.Email, Email = model.Email }; try { var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { await SignInAsync(user, false); // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771 // Send an email with this link // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>"); return RedirectToAction("Index", "Home"); } AddErrors(result); } catch (Exception ex) { throw (ex); } } // If we got this far, something failed, redisplay form return View(model); } [ChildActionOnly] public ActionResult RemoveAccountList() { var linkedAccounts = UserManager.GetLogins(long.Parse(User.Identity.GetUserId())); ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1; return PartialView("_RemoveAccountPartial", linkedAccounts); } [AllowAnonymous] public ActionResult ResetPassword(string code) { if (code == null) { return View("Error"); } return View(); } // // POST: /Account/ResetPassword [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model) { if (ModelState.IsValid) { var user = await UserManager.FindByNameAsync(model.Email); if (user == null) { ModelState.AddModelError("", "No user found."); return View(); } var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password); if (result.Succeeded) { return RedirectToAction("ResetPasswordConfirmation", "Account"); } AddErrors(result); return View(); } // If we got this far, something failed, redisplay form return View(model); } // // GET: /Account/ResetPasswordConfirmation [AllowAnonymous] public ActionResult ResetPasswordConfirmation() { return View(); } #endregion #region Helpers private async Task SignInAsync(MyUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); } private void AddErrors(IdentityResult result) { foreach (var error in result.Errors) { ModelState.AddModelError("", error); } } private bool HasPassword() { var user = UserManager.FindById(long.Parse(User.Identity.GetUserId())); if (user != null) { return user.PasswordHash != null; } return false; } private ActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } #endregion private class ChallengeResult : HttpUnauthorizedResult { #region constructors and destructors public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null) { } public ChallengeResult(string provider, string redirectUri, string userId) { LoginProvider = provider; RedirectUri = redirectUri; UserId = userId; } #endregion #region properties public string LoginProvider { get; set; } public string RedirectUri { get; set; } public string UserId { get; set; } #endregion #region methods public override void ExecuteResult(ControllerContext context) { var properties = new AuthenticationProperties { RedirectUri = RedirectUri }; if (UserId != null) { properties.Dictionary[XsrfKey] = UserId; } context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider); } #endregion } }}
Step 15. 试运行MVC项目,网站正常运行后,先注册用户:
Step 16. 检查目标数据库,用户数据已保存至Web.config文件所指定的目标数据库User表中
海外公司注册、海外银行开户、跨境平台代入驻、VAT、EPR等知识和在线办理:https://www.xlkjsw.com
原标题:定制Asp.NET 5 MVC内建身份验证机制
关键词:ASP.NET
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:
admin#shaoqun.com
(#换成@)。