2013年12月17日 星期二

在ASP.NET MVC5中建置以角色為基礎的授權機制

測試環境: VS2013、MVC5、Windows7




在前一篇貼文中,已探索過如何在MVC5中自定ASP.NET Identity,接下來要來試試在MVC5中如何運用 ASP.NET Identity來設定一個以 "角色"為基礎的授權機制。為了方便起見,簡化了這個認證機制的內容,同時假設這是一個公司內部使用的應用程式,所以拿掉了"註冊"的功能,所有的使用帳號管理都必須透過某一管理權限(Admin)的使用者來進行,也就是說只有具備有 Admin 角色的使用者可以執行”帳戶管理"的功能。


貼文內容:
  • 建立MVC5新專案
  • 修改相關 Models
    • 擴展Identity Management Model
      • 加入新欄位
      • 建立Helper Class
    • 擴展Account Management ViewModels (AccountViewModels.cs)
      • 在RegisterViewModel加入新欄位 及 GetUser method
      • 新增 EditUserViewModel、SelectUserRolesViewModel、SelectRoleEditorViewModel
  • 修改相關 Controllers
    • 修改AccountController 中 Register Method 加入 Authorize attribute
    • 加入 Index Method (ActionResult)
    • 加入 Edit Method (ActionResult)
    • 加入 Delete Method (ActionResult)
    • 加入 UserRoles Method (ActionResult)


  • 修改相關 Views
    • 修改Register.cshtml View
    • 新增Edit、Delete、Index 等方法的Views
    • 新增UserRoles.cshtml View 並 新增程式碼
    • 新增 SelectRoleEditorViewModel.cshtml 在 Shared/EditorTemplates目錄下


  • 在主頁面上新增”帳號管理" 功能按鈕
  • 移除主頁面上的註冊功能
  • 啟動 Migration功能
  • 在Seed()方法中加入建立測試資料的程式碼
  • 更新資料庫
  • 執行結果






建立MVC5新專案


修改相關 Models
1. 擴展Identity Management Model (IdentityModels.cs)
  • 加入新欄位:為使用者資料多加三個屬性欄位,分別是FirstName、LastName、Email。


  • 建立Helper Class:利用Asp.Net Identity API建立一個 Identity Management Helper class: IdentityManager class,包含有建立角色、建立使用者...等功能。


IdentityModels.cs 完整程式
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;


namespace RoleBaseProject.Models
{
   // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
   public class ApplicationUser : IdentityUser
   {
       [Required]
       public string FirstName { get; set; }


       [Required]
       public string LastName { get; set; }
       
       [Required]
       public string Email { get; set; }
   }


   public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
   {
       public ApplicationDbContext()
           : base("DefaultConnection")
       {
       }
   }


   public class IdentityManager
   {
       // 判斷角色是否已在存在
       public bool RoleExists(string name)
       {
           var rm = new RoleManager<IdentityRole>(
               new RoleStore<IdentityRole>(new ApplicationDbContext()));
           return rm.RoleExists(name);
       }
       // 新增角色
       public bool CreateRole(string name)
       {
           var rm = new RoleManager<IdentityRole>(
               new RoleStore<IdentityRole>(new ApplicationDbContext()));
           var idResult = rm.Create(new IdentityRole(name));
           return idResult.Succeeded;
       }
       // 新增角色
       public bool CreateUser(ApplicationUser user, string password)
       {
           var um = new UserManager<ApplicationUser>(
               new UserStore<ApplicationUser>(new ApplicationDbContext()));
           var idResult = um.Create(user, password);
           return idResult.Succeeded;
       }
       // 將使用者加入角色中
       public bool AddUserToRole(string userId, string roleName)
       {
           var um = new UserManager<ApplicationUser>(
               new UserStore<ApplicationUser>(new ApplicationDbContext()));
           var idResult = um.AddToRole(userId, roleName);
           return idResult.Succeeded;
       }
       // 清除使用者的角色設定
       public void ClearUserRoles(string userId)
       {
           var um = new UserManager<ApplicationUser>(
               new UserStore<ApplicationUser>(new ApplicationDbContext()));
           var user = um.FindById(userId);
           var currentRoles = new List<IdentityUserRole>();
           currentRoles.AddRange(user.Roles);
           foreach(var role in currentRoles)
           {
               um.RemoveFromRole(userId, role.Role.Name);
           }
       }
   }
}




2. 擴展Account Management ViewModels (AccountViewModels.cs)
  • 在RegisterViewModel加入新欄位 及 GetUser method


  • 針對要新增的"帳號管理"功能新增三個ViewModel,分別是: EditUserViewModel、SelectUserRolesViewModel、SelectRoleEditorViewModel


AccountViewModels.cs 完整程式
using Microsoft.AspNet.Identity.EntityFramework;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;


namespace RoleBaseProject.Models
{
   public class ExternalLoginConfirmationViewModel
   {
       [Required]
       [Display(Name = "使用者名稱")]
       public string UserName { get; set; }
   }


   public class ManageUserViewModel
   {
       [Required]
       [DataType(DataType.Password)]
       [Display(Name = "目前密碼")]
       public string OldPassword { get; set; }


       [Required]
       [StringLength(100, ErrorMessage = "{0} 的長度至少必須為 {2} 個字元。", MinimumLength = 6)]
       [DataType(DataType.Password)]
       [Display(Name = "新密碼")]
       public string NewPassword { get; set; }


       [DataType(DataType.Password)]
       [Display(Name = "確認新密碼")]
       [Compare("NewPassword", ErrorMessage = "新密碼與確認密碼不相符。")]
       public string ConfirmPassword { get; set; }
   }


   public class LoginViewModel
   {
       [Required]
       [Display(Name = "使用者名稱")]
       public string UserName { get; set; }


       [Required]
       [DataType(DataType.Password)]
       [Display(Name = "密碼")]
       public string Password { get; set; }


       [Display(Name = "記住我?")]
       public bool RememberMe { get; set; }
   }


   public class RegisterViewModel
   {
       [Required]
       [Display(Name = "使用者名稱")]
       public string UserName { get; set; }


       [Required]
       [StringLength(100, ErrorMessage = "{0} 的長度至少必須為 {2} 個字元。", 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; }


       [Required]
       [Display(Name = "First Name")]
       public string FirstName { get; set; }


       [Required]
       [Display(Name = "Last Name")]
       public string LastName { get; set; }


       [Required]
       [Display(Name = "電子郵件信箱")]
       public string Email { get; set; }


       public ApplicationUser GetUser()
       {
           var user = new ApplicationUser()
           {
               UserName = this.UserName,
               FirstName = this.FirstName,
               LastName = this.LastName,
               Email = this.Email,
           };
           return user;
       }
   }


   public class EditUserViewModel
   {
       public EditUserViewModel() { }


       // Allow Initialization with an instance of ApplicationUser:
       public EditUserViewModel(ApplicationUser user)
       {
           this.UserName = user.UserName;
           this.FirstName = user.FirstName;
           this.LastName = user.LastName;
           this.Email = user.Email;
       }


       [Required]
       [Display(Name = "使用者帳號")]
       public string UserName { get; set; }


       [Required]
       [Display(Name = "名")]
       public string FirstName { get; set; }


       [Required]
       [Display(Name = "姓")]
       public string LastName { get; set; }


       [Required]
       [Display(Name = "電子郵件信箱")]
       public string Email { get; set; }
   }



   public class SelectUserRolesViewModel
   {
       public SelectUserRolesViewModel()
       {
           this.Roles = new List<SelectRoleEditorViewModel>();
       }



       // Enable initialization with an instance of ApplicationUser:
       public SelectUserRolesViewModel(ApplicationUser user)
           : this()
       {
           this.UserName = user.UserName;
           this.FirstName = user.FirstName;
           this.LastName = user.LastName;


           var Db = new ApplicationDbContext();


           // Add all available roles to the list of EditorViewModels:
           var allRoles = Db.Roles;
           foreach (var role in allRoles)
           {
               // An EditorViewModel will be used by Editor Template:
               var rvm = new SelectRoleEditorViewModel(role);
               this.Roles.Add(rvm);
           }


           // Set the Selected property to true for those roles for
           // which the current user is a member:
           foreach (var userRole in user.Roles)
           {
               var checkUserRole =
                   this.Roles.Find(r => r.RoleName == userRole.Role.Name);
               checkUserRole.Selected = true;
           }
       }


       public string UserName { get; set; }
       public string FirstName { get; set; }
       public string LastName { get; set; }
       public List<SelectRoleEditorViewModel> Roles { get; set; }
   }


   // Used to display a single role with a checkbox, within a list structure:
   public class SelectRoleEditorViewModel
   {
       public SelectRoleEditorViewModel() { }
       public SelectRoleEditorViewModel(IdentityRole role)
       {
           this.RoleName = role.Name;
       }


       public bool Selected { get; set; }


       [Required]
       public string RoleName { get; set; }
   }


}





修改相關 Controllers
  • 修改AccountController 中 Register Method 加入 Authorize attribute
必須具有Admin 角色者才能執行。


  • 加入 Index Method (ActionResult)


       [Authorize(Roles = "Admin")]
       public ActionResult Index()
       {
           var Db = new ApplicationDbContext();
           var users = Db.Users;
           var model = new List<EditUserViewModel>();
           foreach(var user in users)
           {
               var u = new EditUserViewModel(user);
               model.Add(u);
           }
           return View(model);
       }


  • 加入 Edit Method (ActionResult)


       //
       // GEG: /Account/Edit
       [Authorize(Roles = "Admin")]
       public ActionResult Edit(string id , ManageMessageId? Message = null)
       {
           var Db = new ApplicationDbContext();
           var user = Db.Users.First(u => u.UserName == id);
           var model = new EditUserViewModel(user);
           ViewBag.MessageId = Message;
           return View(model);
       }
       //
       // POST: /Account/Edit
       [HttpPost]
       [Authorize(Roles = "Admin")]
       [ValidateAntiForgeryToken]
       public async Task<ActionResult> Edit(EditUserViewModel model)
       {
           if (ModelState.IsValid)
           {
               var Db = new ApplicationDbContext();
               var user = Db.Users.First(u => u.UserName == model.UserName);
               user.FirstName = model.FirstName;
               user.LastName = model.LastName;
               user.Email = model.Email;
               Db.Entry(user).State = System.Data.Entity.EntityState.Modified;
               await Db.SaveChangesAsync();
               return RedirectToAction("Index");
           }
           return View(model);
       }


  • 加入 Delete Method (ActionResult)


       //
       // GEG: /Account/Delete
       [Authorize(Roles="Admin")]
       public ActionResult Delete(string id = null)
       {
           var Db = new ApplicationDbContext();
           var user = Db.Users.First(u => u.UserName == id);
           var model = new EditUserViewModel(user);
           return View(model);
       }
       //
       // POST: /Account/Delete
       [HttpPost, ActionName("Delete")]
       [Authorize(Roles = "Admin")]
       [ValidateAntiForgeryToken]
       public ActionResult DeleteConfirmed(string id)
       {
           var Db = new ApplicationDbContext();
           var user = Db.Users.First(u => u.UserName == id);
           Db.Users.Remove(user);
           Db.SaveChanges();
           return RedirectToAction("Index");
       }


  • 加入 UserRoles Method (ActionResult)


       //
       // GEG: /Account/UserRoles
       [Authorize(Roles = "Admin")]
       public ActionResult UserRoles(string id)
       {
           var Db = new ApplicationDbContext();
           var user = Db.Users.First(u => u.UserName == id);
           var model = new SelectUserRolesViewModel(user);
           return View(model);
       }


       //
       // POST: /Account/UserRoles
       [HttpPost]
       [Authorize(Roles = "Admin")]
       [ValidateAntiForgeryToken]
       public ActionResult UserRoles(SelectUserRolesViewModel model)
       {
           if (ModelState.IsValid)
           {
               var idManager = new IdentityManager();
               var Db = new ApplicationDbContext();
               var user = Db.Users.First(u => u.UserName == model.UserName);
               idManager.ClearUserRoles(user.Id);
               foreach (var role in model.Roles)
               {
                   if (role.Selected)
                   {
                       idManager.AddUserToRole(user.Id, role.RoleName);
                   }
               }
               return RedirectToAction("index");
           }
           return View();
       }





AccountController.cs 完整程式
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using RoleBaseProject.Models;


namespace RoleBaseProject.Controllers
{
   [Authorize]
   public class AccountController : Controller
   {
       public AccountController()
           : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
       {
       }


       public AccountController(UserManager<ApplicationUser> userManager)
       {
           UserManager = userManager;
       }


       public UserManager<ApplicationUser> UserManager { get; private set; }


       //
       // 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.UserName, model.Password);
               if (user != null)
               {
                   await SignInAsync(user, model.RememberMe);
                   return RedirectToLocal(returnUrl);
               }
               else
               {
                   ModelState.AddModelError("", "Invalid username or password.");
               }
           }


           // 如果執行到這裡,發生某項失敗,則重新顯示表單
           return View(model);
       }


       //
       // GET: /Account/Register
       [Authorize(Roles="Admin")]
       public ActionResult Register()
       {
           return View();
       }


       //
       // POST: /Account/Register
       [HttpPost]
       [Authorize(Roles = "Admin")]
       [ValidateAntiForgeryToken]
       public async Task<ActionResult> Register(RegisterViewModel model)
       {
           if (ModelState.IsValid)
           {
               var user = new ApplicationUser() {
                   UserName = model.UserName,
                   FirstName = model.FirstName,
                   LastName = model.LastName,
                   Email = model.Email,
               };
               var result = await UserManager.CreateAsync(user, model.Password);
               if (result.Succeeded)
               {
                   await SignInAsync(user, isPersistent: false);
                   return RedirectToAction("Index", "Home");
               }
               else
               {
                   AddErrors(result);
               }
           }


           // 如果執行到這裡,發生某項失敗,則重新顯示表單
           return View(model);
       }


       //
       // GEG: /Account/Index
       [Authorize(Roles = "Admin")]
       public ActionResult Index()
       {
           var Db = new ApplicationDbContext();
           var users = Db.Users;
           var model = new List<EditUserViewModel>();
           foreach(var user in users)
           {
               var u = new EditUserViewModel(user);
               model.Add(u);
           }
           return View(model);
       }


       //
       // GEG: /Account/Edit
       [Authorize(Roles = "Admin")]
       public ActionResult Edit(string id , ManageMessageId? Message = null)
       {
           var Db = new ApplicationDbContext();
           var user = Db.Users.First(u => u.UserName == id);
           var model = new EditUserViewModel(user);
           ViewBag.MessageId = Message;
           return View(model);
       }
       //
       // POST: /Account/Edit
       [HttpPost]
       [Authorize(Roles = "Admin")]
       [ValidateAntiForgeryToken]
       public async Task<ActionResult> Edit(EditUserViewModel model)
       {
           if (ModelState.IsValid)
           {
               var Db = new ApplicationDbContext();
               var user = Db.Users.First(u => u.UserName == model.UserName);
               user.FirstName = model.FirstName;
               user.LastName = model.LastName;
               user.Email = model.Email;
               Db.Entry(user).State = System.Data.Entity.EntityState.Modified;
               await Db.SaveChangesAsync();
               return RedirectToAction("Index");
           }
           return View(model);
       }


       //
       // GEG: /Account/Delete
       [Authorize(Roles="Admin")]
       public ActionResult Delete(string id = null)
       {
           var Db = new ApplicationDbContext();
           var user = Db.Users.First(u => u.UserName == id);
           var model = new EditUserViewModel(user);
           return View(model);
       }
       //
       // POST: /Account/Delete
       [HttpPost, ActionName("Delete")]
       [Authorize(Roles = "Admin")]
       [ValidateAntiForgeryToken]
       public ActionResult DeleteConfirmed(string id)
       {
           var Db = new ApplicationDbContext();
           var user = Db.Users.First(u => u.UserName == id);
           Db.Users.Remove(user);
           Db.SaveChanges();
           return RedirectToAction("Index");
       }
       
       //
       // GEG: /Account/UserRoles
       [Authorize(Roles = "Admin")]
       public ActionResult UserRoles(string id)
       {
           var Db = new ApplicationDbContext();
           var user = Db.Users.First(u => u.UserName == id);
           var model = new SelectUserRolesViewModel(user);
           return View(model);
       }


       //
       // POST: /Account/UserRoles
       [HttpPost]
       [Authorize(Roles = "Admin")]
       [ValidateAntiForgeryToken]
       public ActionResult UserRoles(SelectUserRolesViewModel model)
       {
           if (ModelState.IsValid)
           {
               var idManager = new IdentityManager();
               var Db = new ApplicationDbContext();
               var user = Db.Users.First(u => u.UserName == model.UserName);
               idManager.ClearUserRoles(user.Id);
               foreach (var role in model.Roles)
               {
                   if (role.Selected)
                   {
                       idManager.AddUserToRole(user.Id, role.RoleName);
                   }
               }
               return RedirectToAction("index");
           }
           return View();
       }


       //
       // POST: /Account/Disassociate
       [HttpPost]
       [ValidateAntiForgeryToken]
       public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
       {
           ManageMessageId? message = null;
           IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
           if (result.Succeeded)
           {
               message = ManageMessageId.RemoveLoginSuccess;
           }
           else
           {
               message = ManageMessageId.Error;
           }
           return RedirectToAction("Manage", new { Message = message });
       }


       //
       // GET: /Account/Manage
       public ActionResult Manage(ManageMessageId? message)
       {
           ViewBag.StatusMessage =
               message == ManageMessageId.ChangePasswordSuccess ? "您的密碼已變更。"
               : message == ManageMessageId.SetPasswordSuccess ? "已設定您的密碼。"
               : message == ManageMessageId.RemoveLoginSuccess ? "已移除外部登入。"
               : message == ManageMessageId.Error ? "發生錯誤。"
               : "";
           ViewBag.HasLocalPassword = HasPassword();
           ViewBag.ReturnUrl = Url.Action("Manage");
           return View();
       }


       //
       // POST: /Account/Manage
       [HttpPost]
       [ValidateAntiForgeryToken]
       public async Task<ActionResult> Manage(ManageUserViewModel model)
       {
           bool hasPassword = HasPassword();
           ViewBag.HasLocalPassword = hasPassword;
           ViewBag.ReturnUrl = Url.Action("Manage");
           if (hasPassword)
           {
               if (ModelState.IsValid)
               {
                   IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
                   if (result.Succeeded)
                   {
                       return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
                   }
                   else
                   {
                       AddErrors(result);
                   }
               }
           }
           else
           {
               // User does not have a password so remove any validation errors caused by a missing OldPassword field
               ModelState state = ModelState["OldPassword"];
               if (state != null)
               {
                   state.Errors.Clear();
               }


               if (ModelState.IsValid)
               {
                   IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
                   if (result.Succeeded)
                   {
                       return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
                   }
                   else
                   {
                       AddErrors(result);
                   }
               }
           }


           // 如果執行到這裡,發生某項失敗,則重新顯示表單
           return View(model);
       }


       //
       // POST: /Account/ExternalLogin
       [HttpPost]
       [AllowAnonymous]
       [ValidateAntiForgeryToken]
       public ActionResult ExternalLogin(string provider, string returnUrl)
       {
           // 要求重新導向至外部登入提供者
           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, isPersistent: false);
               return RedirectToLocal(returnUrl);
           }
           else
           {
               // 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 { UserName = loginInfo.DefaultUserName });
           }
       }


       //
       // 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(User.Identity.GetUserId(), loginInfo.Login);
           if (result.Succeeded)
           {
               return RedirectToAction("Manage");
           }
           return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
       }


       //
       // 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)
           {
               // 從外部登入提供者處取得使用者資訊
               var info = await AuthenticationManager.GetExternalLoginInfoAsync();
               if (info == null)
               {
                   return View("ExternalLoginFailure");
               }
               var user = new ApplicationUser() { UserName = model.UserName };
               var result = await UserManager.CreateAsync(user);
               if (result.Succeeded)
               {
                   result = await UserManager.AddLoginAsync(user.Id, info.Login);
                   if (result.Succeeded)
                   {
                       await SignInAsync(user, isPersistent: false);
                       return RedirectToLocal(returnUrl);
                   }
               }
               AddErrors(result);
           }


           ViewBag.ReturnUrl = returnUrl;
           return View(model);
       }


       //
       // POST: /Account/LogOff
       [HttpPost]
       [ValidateAntiForgeryToken]
       public ActionResult LogOff()
       {
           AuthenticationManager.SignOut();
           return RedirectToAction("Index", "Home");
       }


       //
       // GET: /Account/ExternalLoginFailure
       [AllowAnonymous]
       public ActionResult ExternalLoginFailure()
       {
           return View();
       }


       [ChildActionOnly]
       public ActionResult RemoveAccountList()
       {
           var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId());
           ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
           return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
       }


       protected override void Dispose(bool disposing)
       {
           if (disposing && UserManager != null)
           {
               UserManager.Dispose();
               UserManager = null;
           }
           base.Dispose(disposing);
       }


       #region Helper
       // Used for XSRF protection when adding external logins
       private const string XsrfKey = "XsrfId";


       private IAuthenticationManager AuthenticationManager
       {
           get
           {
               return HttpContext.GetOwinContext().Authentication;
           }
       }


       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);
       }


       private void AddErrors(IdentityResult result)
       {
           foreach (var error in result.Errors)
           {
               ModelState.AddModelError("", error);
           }
       }


       private bool HasPassword()
       {
           var user = UserManager.FindById(User.Identity.GetUserId());
           if (user != null)
           {
               return user.PasswordHash != null;
           }
           return false;
       }


       public enum ManageMessageId
       {
           ChangePasswordSuccess,
           SetPasswordSuccess,
           RemoveLoginSuccess,
           Error
       }


       private ActionResult RedirectToLocal(string returnUrl)
       {
           if (Url.IsLocalUrl(returnUrl))
           {
               return Redirect(returnUrl);
           }
           else
           {
               return RedirectToAction("Index", "Home");
           }
       }


       private class ChallengeResult : HttpUnauthorizedResult
       {
           public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null)
           {
           }


           public ChallengeResult(string provider, string redirectUri, string userId)
           {
               LoginProvider = provider;
               RedirectUri = redirectUri;
               UserId = userId;
           }


           public string LoginProvider { get; set; }
           public string RedirectUri { get; set; }
           public string UserId { get; set; }


           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
   }
}



修改相關 Views
  • 修改Register.cshtml View


@model RoleBaseProject.Models.RegisterViewModel
@{
   ViewBag.Title = "註冊";
}


<h2>@ViewBag.Title.</h2>


@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
   @Html.AntiForgeryToken()
   <h4>建立新的帳戶。</h4>
   <hr />
   @Html.ValidationSummary()
   <div class="form-group">
       @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
       <div class="col-md-10">
           @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
       </div>
   </div>
   <div class="form-group">
       @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
       <div class="col-md-10">
           @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
       </div>
   </div>
   <div class="form-group">
       @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
       <div class="col-md-10">
           @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
       </div>
   </div>
   <div class="form-group">
       @Html.LabelFor(m => m.FirstName, new { @class = "col-md-2 control-label" })
       <div class="col-md-10">
           @Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
       </div>
   </div>
   <div class="form-group">
       @Html.LabelFor(m => m.LastName, new { @class = "col-md-2 control-label" })
       <div class="col-md-10">
           @Html.TextBoxFor(m => m.LastName, new { @class = "form-control" })
       </div>
   </div>
   <div class="form-group">
       @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
       <div class="col-md-10">
           @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
       </div>
   </div>
   <div class="form-group">
       <div class="col-md-offset-2 col-md-10">
           <input type="submit" class="btn btn-default" value="註冊" />
       </div>
   </div>
}


@section Scripts {
   @Scripts.Render("~/bundles/jqueryval")
}


  • 新增Edit、Delete、Index 等方法的Views


新增Edit Views












用以下內容取代原程式


@model RoleBaseProject.Models.EditUserViewModel


@{
   ViewBag.Title = "資料者資料編輯";
}


<h2>資料者資料編輯</h2>



@using (Html.BeginForm())
{
   @Html.AntiForgeryToken()
   
   <div class="form-horizontal">
       <h4>修改原有帳號資料</h4>
       <hr />
       @Html.ValidationSummary(true)
       <div class="form-group">
           @Html.LabelFor(model => model.UserName, new { @class = "control-label col-md-2" })
           <div class="col-md-10">
               @Html.EditorFor(model => model.UserName)
               @Html.ValidationMessageFor(model => model.UserName)
           </div>
       </div>


       <div class="form-group">
           @Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" })
           <div class="col-md-10">
               @Html.EditorFor(model => model.FirstName)
               @Html.ValidationMessageFor(model => model.FirstName)
           </div>
       </div>


       <div class="form-group">
           @Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" })
           <div class="col-md-10">
               @Html.EditorFor(model => model.LastName)
               @Html.ValidationMessageFor(model => model.LastName)
           </div>
       </div>


       <div class="form-group">
           @Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" })
           <div class="col-md-10">
               @Html.EditorFor(model => model.Email)
               @Html.ValidationMessageFor(model => model.Email)
           </div>
       </div>


       <div class="form-group">
           <div class="col-md-offset-2 col-md-10">
               <input type="submit" value="存檔" class="btn btn-default" />
           </div>
       </div>
   </div>
}


<div>
   @Html.ActionLink("回到使用者清單畫面", "Index")
</div>


@section Scripts {
   @Scripts.Render("~/bundles/jqueryval")
}




新增Delete的Views




用以下內容取代原程式


@model RoleBaseProject.Models.EditUserViewModel


<h3>使用者帳號刪除</h3>
<div>
   <h4>確定要刪除這個使用者帳號?</h4>
   <hr />
   <dl class="dl-horizontal">
       <dt>
           @Html.DisplayNameFor(model => model.UserName)
       </dt>


       <dd>
           @Html.DisplayFor(model => model.UserName)
       </dd>


       <dt>
           @Html.DisplayNameFor(model => model.FirstName)
       </dt>


       <dd>
           @Html.DisplayFor(model => model.FirstName)
       </dd>


       <dt>
           @Html.DisplayNameFor(model => model.LastName)
       </dt>


       <dd>
           @Html.DisplayFor(model => model.LastName)
       </dd>


       <dt>
           @Html.DisplayNameFor(model => model.Email)
       </dt>


       <dd>
           @Html.DisplayFor(model => model.Email)
       </dd>


   </dl>


   @using (Html.BeginForm()) {
       @Html.AntiForgeryToken()


       <div class="form-actions no-color">
           <input type="submit" value="刪除" class="btn btn-default" /> |
           @Html.ActionLink("回到使用者清單畫面", "Index")
       </div>
   }
</div>



新增Index Views


用以下內容取代原程式
@model IEnumerable<RoleBaseProject.Models.EditUserViewModel>


@{
   ViewBag.Title = "帳號管理";
}


<h2>帳號管理</h2>


<p>
   @Html.ActionLink("新增帳號", "Register")
</p>
<table class="table">
   <tr>
       <th>
           @Html.DisplayNameFor(model => model.UserName)
       </th>
       <th>
           @Html.DisplayNameFor(model => model.FirstName)
       </th>
       <th>
           @Html.DisplayNameFor(model => model.LastName)
       </th>
       <th>
           @Html.DisplayNameFor(model => model.Email)
       </th>
       <th></th>
   </tr>


@foreach (var item in Model) {
   <tr>
       <td>
           @Html.DisplayFor(modelItem => item.UserName)
       </td>
       <td>
           @Html.DisplayFor(modelItem => item.FirstName)
       </td>
       <td>
           @Html.DisplayFor(modelItem => item.LastName)
       </td>
       <td>
           @Html.DisplayFor(modelItem => item.Email)
       </td>
       <td>
           @Html.ActionLink("編輯", "Edit", new {  id=item.UserName  }) |
           @Html.ActionLink("角色", "UserRoles", new { id=item.UserName }) |
           @Html.ActionLink("刪除", "Delete", new { id=item.UserName })
       </td>
   </tr>
}


</table>




新增好的三個View



  • 新增UserRoles.cshtml View 並 新增程式碼



用以下內容取代原程式
@model RoleBaseProject.Models.SelectUserRolesViewModel
@{
   ViewBag.Title = "使用者角色";
}


<h2>使用者角色 @Html.DisplayFor(model => model.UserName)</h2>
<hr />


@using (Html.BeginForm("UserRoles", "Account", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
{
   @Html.AntiForgeryToken()


   <div class="form-horizontal">
       @Html.ValidationSummary(true)
       <div class="form-group">
           <div class="col-md-10">
               @Html.HiddenFor(model => model.UserName)
           </div>
       </div>


       <h4>選擇要加入的角色</h4>
       <br />
       <hr />


       <table>
           <tr>
               <th>
                   勾選
               </th>
               <th>
                   角色
               </th>
           </tr>
           @Html.EditorFor(model => model.Roles)
       </table>
       <br />
       <hr />


       <div class="form-group">
           <div class="col-md-offset-2 col-md-10">
               <input type="submit" value="存檔" class="btn btn-default" />
           </div>
       </div>
   </div>
}



  • 新增SelectRoleEditorViewModel.cshtml 在 Shared/EditorTemplates目錄下



程式如下:
@model RoleBaseProject.Models.SelectRoleEditorViewModel
@Html.HiddenFor(model => model.RoleName)
<tr>
   <td style="text-align:center">
       @Html.CheckBoxFor(model => model.Selected)
   </td>
   <td>
       @Html.DisplayFor(model => model.RoleName)
   </td>
</tr>


在主頁面上新增”帳號管理" 功能按鈕



移除主頁面上的註冊功能



啟動 Migration功能



在Seed()方法中加入建立測試資料的程式碼


完整程式:
namespace RoleBaseProject.Migrations
{
   using RoleBaseProject.Models;
   using System;
   using System.Data.Entity;
   using System.Data.Entity.Migrations;
   using System.Linq;


   internal sealed class Configuration : DbMigrationsConfiguration<RoleBaseProject.Models.ApplicationDbContext>
   {
       public Configuration()
       {
           AutomaticMigrationsEnabled = false;
       }


       protected override void Seed(RoleBaseProject.Models.ApplicationDbContext context)
       {
           //  This method will be called after migrating to the latest version.


           //  You can use the DbSet<T>.AddOrUpdate() helper extension method
           //  to avoid creating duplicate seed data. E.g.
           //
           //    context.People.AddOrUpdate(
           //      p => p.FullName,
           //      new Person { FullName = "Andrew Peters" },
           //      new Person { FullName = "Brice Lambson" },
           //      new Person { FullName = "Rowan Miller" }
           //    );
           //
           this.AddUserAndRoles();
       }


       bool AddUserAndRoles()
       {
           bool success = false;


           var idManager = new IdentityManager();
           success = idManager.CreateRole("Admin");
           if (!success == true) return success;


           success = idManager.CreateRole("CanEdit");
           if (!success == true) return success;


           success = idManager.CreateRole("User");
           if (!success) return success;



           var newUser = new ApplicationUser()
           {
               UserName = "jatten",
               FirstName = "John",
               LastName = "Atten",
               Email = "jatten@typecastexception.com"
           };


           success = idManager.CreateUser(newUser, "Password1");
           if (!success) return success;


           success = idManager.AddUserToRole(newUser.Id, "Admin");
           if (!success) return success;


           success = idManager.AddUserToRole(newUser.Id, "CanEdit");
           if (!success) return success;


           success = idManager.AddUserToRole(newUser.Id, "User");
           if (!success) return success;


           return success;
       }
   }
}


更新資料庫




執行結果



以具有Admin角色的使用者登入後執行帳戶管理功能:

1 則留言: