在ASP.NET MVC5中建置以角色为基础的授权机制

在前一篇贴文中,已探索过如何在MVC5中自定ASP.NET Identity,接下来要来试试在MVC5中如何运用 ASP.NET Identity来设定一个以 "角色"为基础的授权机制。为了方便起见,简化了这个认证机制的内容,同时假设这是一个公司内部使用的应用程序,所以拿掉了"注册"的功能,所有的使用账 号管理都必须透过某一管理权限(Admin)的使用者来进行,也就是说只有具备有 Admin 角色的使用者可以执行”账户管理"的功能。
贴文内容:
建立MVC5新项目
修改相关 Mdels
扩展Identity Management Mdel 
加入新字段
建立Helper Class
扩展Accunt Management ViewMdels (AccuntViewMdels.cs)
在RegisterViewMdel加入新字段 及 GetUser methd
新增 EditUserViewMdel、SelectUserRlesViewMdel、SelectRleEditrViewMdel
修改相关 Cntrllers
修改AccuntCntrller 中 Register Methd 加入 Authrize attribute
加入 Index Methd (ActinResult)
加入 Edit Methd (ActinResult)
加入 Delete Methd (ActinResult)
加入 UserRles Methd (ActinResult)
修改相关 Views
修改Register.cshtml View 
新增Edit、Delete、Index 等方法的Views
新增UserRles.cshtml View 并 新增程序代码
新增 SelectRleEditrViewMdel.cshtml 在 Shared/EditrTemplates目录下
在主页面上新增”账号管理" 功能按钮
移除主页面上的注册功能
启动 Migratin功能
在Seed()方法中加入建立测试数据的程序代码
更新数据库
执行结果

 
建立MVC5新项目
 
在ASP.NET MVC5中建置以角色为基础的授权机制


在ASP.NET MVC5中建置以角色为基础的授权机制
 
修改相关 Models
 
1. 扩展Identity Management Model (IdentityModels.cs)
 
加入新字段:为使用者数据多加三个属性字段,分别是FirstName、LastName、Email。
 
在ASP.NET MVC5中建置以角色为基础的授权机制
 
建立Helper Class:利用Asp.Net Identity API建立一个 Identity Management Helper class: IdentityManager class,包含有建立角色、建立使用者...等功能。
 
在ASP.NET MVC5中建置以角色为基础的授权机制
 
IdentityModels.cs 完整程序

01.using Microsoft.AspNet.Identity;
02.using Microsoft.AspNet.Identity.EntityFramework;
03.using System.Collections.Generic;
04.using System.ComponentModel.DataAnnotations;
05. 
06.namespace RoleBaseProject.Models
07.{
08.// 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.
09.public class ApplicationUser : IdentityUser
10.{
11.[Required]
12.public string FirstName { get; set; }
13. 
14.[Required]
15.public string LastName { get; set; }
16.[Required]
17.public string Email { get; set; }
18.}
19. 
20.public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
21.{
22.public ApplicationDbContext()
23.: base("DefaultConnection")
24.{
25.}
26.}
27. 
28.public class IdentityManager
29.{
30.// 判断角色是否已在存在
31.public bool RoleExists(string name)
32.{
33.var rm = new RoleManager<IdentityRole>(
34.new RoleStore<IdentityRole>(new ApplicationDbContext()));
35.return rm.RoleExists(name);
36.}
37.// 新增角色
38.public bool CreateRole(string name)
39.{
40.var rm = new RoleManager<IdentityRole>(
41.new RoleStore<IdentityRole>(new ApplicationDbContext()));
42.var idResult = rm.Create(new IdentityRole(name));
43.return idResult.Succeeded;
44.}
45.// 新增角色
46.public bool CreateUser(ApplicationUser user, string pass<a href="http://www.it165.net/edu/ebg/" target="_blank" class="keylink">word</a>)
47.{
48.var um = new UserManager<ApplicationUser>(
49.new UserStore<ApplicationUser>(new ApplicationDbContext()));
50.var idResult = um.Create(user, pass<a href="http://www.it165.net/edu/ebg/" target="_blank" class="keylink">word</a>);
51.return idResult.Succeeded;
52.}
53.// 将使用者加入角色中
54.public bool AddUserToRole(string userId, string roleName)
55.{
56.var um = new UserManager<ApplicationUser>(
57.new UserStore<ApplicationUser>(new ApplicationDbContext()));
58.var idResult = um.AddToRole(userId, roleName);
59.return idResult.Succeeded;
60.}
61.// 清除使用者的角色设定
62.public void ClearUserRoles(string userId)
63.{
64.var um = new UserManager<ApplicationUser>(
65.new UserStore<ApplicationUser>(new ApplicationDbContext()));
66.var user = um.FindById(userId);
67.var currentRoles = new List<IdentityUserRole>();
68.currentRoles.AddRange(user.Roles);
69.foreach(var role in currentRoles)
70.{
71.um.RemoveFromRole(userId, role.Role.Name);
72.}
73.}
74.}
75.}
2. 扩展Account Management ViewModels (AccountViewModels.cs)
 
在RegisterViewModel加入新字段 及 GetUser method
 
在ASP.NET MVC5中建置以角色为基础的授权机制
 
 
针对要新增的"账号管理"功能新增三个ViewModel,分别是: EditUserViewModel、SelectUserRolesViewModel、SelectRoleEditorViewModel
 
AccountViewModels.cs 完整程序


001.using Microsoft.AspNet.Identity.EntityFramework;
002.using System.Collections.Generic;
003.using System.ComponentModel.DataAnnotations;
004. 
005.namespace RoleBaseProject.Models
006.{
007.public class ExternalLoginConfirmationViewModel
008.{
009.[Required]
010.[Display(Name = "使用者名称")]
011.public string UserName { get; set; }
012.}
013. 
014.public class ManageUserViewModel
015.{
016.[Required]
017.[DataType(DataType.Password)]
018.[Display(Name = "目前密码")]
019.public string OldPassword { get; set; }
020. 
021.[Required]
022.[StringLength(100, ErrorMessage = "{0} 的长度至少必须为 {2} 个字符。", MinimumLength = 6)]
023.[DataType(DataType.Password)]
024.[Display(Name = "新密码")]
025.public string NewPassword { get; set; }
026. 
027.[DataType(DataType.Password)]
028.[Display(Name = "确认新密码")]
029.[Compare("NewPassword", ErrorMessage = "新密码与确认密码不相符。")]
030.public string ConfirmPassword { get; set; }
031.}
032. 
033.public class LoginViewModel
034.{
035.[Required]
036.[Display(Name = "使用者名称")]
037.public string UserName { get; set; }
038. 
039.[Required]
040.[DataType(DataType.Password)]
041.[Display(Name = "密码")]
042.public string Password { get; set; }
043. 
044.[Display(Name = "记住我?")]
045.public bool RememberMe { get; set; }
046.}
047. 
048.public class RegisterViewModel
049.{
050.[Required]
051.[Display(Name = "使用者名称")]
052.public string UserName { get; set; }
053. 
054.[Required]
055.[StringLength(100, ErrorMessage = "{0} 的长度至少必须为 {2} 个字符。", MinimumLength = 6)]
056.[DataType(DataType.Password)]
057.[Display(Name = "密码")]
058.public string Password { get; set; }
059. 
060.[DataType(DataType.Password)]
061.[Display(Name = "确认密码")]
062.[Compare("Password", ErrorMessage = "密码和确认密码不相符。")]
063.public string ConfirmPassword { get; set; }
064. 
065.[Required]
066.[Display(Name = "First Name")]
067.public string FirstName { get; set; }
068. 
069.[Required]
070.[Display(Name = "Last Name")]
071.public string LastName { get; set; }
072. 
073.[Required]
074.[Display(Name = "电子邮件信箱")]
075.public string Email { get; set; }
076. 
077.public ApplicationUser GetUser()
078.{
079.var user = new ApplicationUser()
080.{
081.UserName = this.UserName,
082.FirstName = this.FirstName,
083.LastName = this.LastName,
084.Email = this.Email,
085.};
086.return user;
087.}
088.}
089. 
090.public class EditUserViewModel
091.{
092.public EditUserViewModel() { }
093. 
094.// Allow Initialization with an instance of ApplicationUser:
095.public EditUserViewModel(ApplicationUser user)
096.{
097.this.UserName = user.UserName;
098.this.FirstName = user.FirstName;
099.this.LastName = user.LastName;
100.this.Email = user.Email;
101.}
102. 
103.[Required]
104.[Display(Name = "使用者账号")]
105.public string UserName { get; set; }
106. 
107.[Required]
108.[Display(Name = "名")]
109.public string FirstName { get; set; }
110. 
111.[Required]
112.[Display(Name = "姓")]
113.public string LastName { get; set; }
114. 
115.[Required]
116.[Display(Name = "电子邮件信箱")]
117.public string Email { get; set; }
118.}
119. 
120.public class SelectUserRolesViewModel
121.{
122.public SelectUserRolesViewModel()
123.{
124.this.Roles = new List<SelectRoleEditorViewModel>();
125.}
126. 
127.// Enable initialization with an instance of ApplicationUser:
128.public SelectUserRolesViewModel(ApplicationUser user)
129.: this()
130.{
131.this.UserName = user.UserName;
132.this.FirstName = user.FirstName;
133.this.LastName = user.LastName;
134. 
135.var Db = new ApplicationDbContext();
136. 
137.// Add all available roles to the list of EditorViewModels:
138.var allRoles = Db.Roles;
139.foreach (var role in allRoles)
140.{
141.// An EditorViewModel will be used by Editor Template:
142.var rvm = new SelectRoleEditorViewModel(role);
143.this.Roles.Add(rvm);
144.}
145. 
146.// Set the Selected property to true for those roles for
147.// which the current user is a member:
148.foreach (var userRole in user.Roles)
149.{
150.var checkUserRole =
151.this.Roles.Find(r => r.RoleName == userRole.Role.Name);
152.checkUserRole.Selected = true;
153.}
154.}
155. 
156.public string UserName { get; set; }
157.public string FirstName { get; set; }
158.public string LastName { get; set; }
159.public List<SelectRoleEditorViewModel> Roles { get; set; }
160.}
161. 
162.// Used to display a single role with a checkbox, within a list structure:
163.public class SelectRoleEditorViewModel
164.{
165.public SelectRoleEditorViewModel() { }
166.public SelectRoleEditorViewModel(IdentityRole role)
167.{
168.this.RoleName = role.Name;
169.}
170. 
171.public bool Selected { get; set; }
172. 
173.[Required]
174.public string RoleName { get; set; }
175.}
176. 
177.}
修改相关 Controllers
 
修改AccountController 中 Register Method 加入 Authorize attribute
 
在ASP.NET MVC5中建置以角色为基础的授权机制
 
 

必须具有Admin 角色者才能执行。

 
加入 Index Method (ActionResult)


01.[Authorize(Roles = "Admin")]
02.public ActionResult Index()
03.{
04.var Db = new ApplicationDbContext();
05.var users = Db.Users;
06.var model = new List<EditUserViewModel>();
07.foreach(var user in users)
08.{
09.var u = new EditUserViewModel(user);
10.model.Add(u);
11.}
12.return View(model);
13.}

加入 Edit Method (ActionResult)


 

01.//
02.// GEG: /Account/Edit
03.[Authorize(Roles = "Admin")]
04.public ActionResult Edit(string id , ManageMessageId? Message = null)
05.{
06.var Db = new ApplicationDbContext();
07.var user = Db.Users.First(u => u.UserName == id);
08.var model = new EditUserViewModel(user);
09.ViewBag.MessageId = Message;
10.return View(model);
11.}
12.//
13.// POST: /Account/Edit
14.[HttpPost]
15.[Authorize(Roles = "Admin")]
16.[ValidateAntiForgeryToken]
17.public async Task<ActionResult> Edit(EditUserViewModel model)
18.{
19.if (ModelState.IsValid)
20.{
21.var Db = new ApplicationDbContext();
22.var user = Db.Users.First(u => u.UserName == model.UserName);
23.user.FirstName = model.FirstName;
24.user.LastName = model.LastName;
25.user.Email = model.Email;
26.Db.Entry(user).State = System.Data.Entity.EntityState.Modified;
27.await Db.SaveChangesAsync();
28.return RedirectToAction("Index");
29.}
30.return View(model);
31.}


加入 Delete Method (ActionResult)


 

01.//
02.// GEG: /Account/Delete
03.[Authorize(Roles="Admin")]
04.public ActionResult Delete(string id = null)
05.{
06.var Db = new ApplicationDbContext();
07.var user = Db.Users.First(u => u.UserName == id);
08.var model = new EditUserViewModel(user);
09.return View(model);
10.}
11.//
12.// POST: /Account/Delete
13.[HttpPost, ActionName("Delete")]
14.[Authorize(Roles = "Admin")]
15.[ValidateAntiForgeryToken]
16.public ActionResult DeleteConfirmed(string id)
17.{
18.var Db = new ApplicationDbContext();
19.var user = Db.Users.First(u => u.UserName == id);
20.Db.Users.Remove(user);
21.Db.SaveChanges();
22.return RedirectToAction("Index");
23.}

加入 UserRoles Method (ActionResult)
 

01.//
02.// GEG: /Account/UserRoles
03.[Authorize(Roles = "Admin")]
04.public ActionResult UserRoles(string id)
05.{
06.var Db = new ApplicationDbContext();
07.var user = Db.Users.First(u => u.UserName == id);
08.var model = new SelectUserRolesViewModel(user);
09.return View(model);
10.}
11. 
12.//
13.// POST: /Account/UserRoles
14.[HttpPost]
15.[Authorize(Roles = "Admin")]
16.[ValidateAntiForgeryToken]
17.public ActionResult UserRoles(SelectUserRolesViewModel model)
18.{
19.if (ModelState.IsValid)
20.{
21.var idManager = new IdentityManager();
22.var Db = new ApplicationDbContext();
23.var user = Db.Users.First(u => u.UserName == model.UserName);
24.idManager.ClearUserRoles(user.Id);
25.foreach (var role in model.Roles)
26.{
27.if (role.Selected)
28.{
29.idManager.AddUserToRole(user.Id, role.RoleName);
30.}
31.}
32.return RedirectToAction("index");
33.}
34.return View();
35.}

AccountController.cs 完整程序


 

001.using System;
002.using System.Collections.Generic;
003.using System.Linq;
004.using System.Security.Claims;
005.using System.Threading.Tasks;
006.using System.Web;
007.using System.Web.Mvc;
008.using Microsoft.AspNet.Identity;
009.using Microsoft.AspNet.Identity.EntityFramework;
010.using Microsoft.Owin.Security;
011.using RoleBaseProject.Models;
012. 
013.namespace RoleBaseProject.Controllers
014.{
015.[Authorize]
016.public class AccountController : Controller
017.{
018.public AccountController()
019.: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
020.{
021.}
022. 
023.public AccountController(UserManager<ApplicationUser> userManager)
024.{
025.UserManager = userManager;
026.}
027. 
028.public UserManager<ApplicationUser> UserManager { get; private set; }
029. 
030.//
031.// GET: /Account/Login
032.[AllowAnonymous]
033.public ActionResult Login(string returnUrl)
034.{
035.ViewBag.ReturnUrl = returnUrl;
036.return View();
037.}
038. 
039.//
040.// POST: /Account/Login
041.[HttpPost]
042.[AllowAnonymous]
043.[ValidateAntiForgeryToken]
044.public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
045.{
046.if (ModelState.IsValid)
047.{
048.var user = await UserManager.FindAsync(model.UserName, model.Password);
049.if (user != null)
050.{
051.await SignInAsync(user, model.RememberMe);
052.return RedirectToLocal(returnUrl);
053.}
054.else
055.{
056.ModelState.AddModelError("", "Invalid username or password.");
057.}
058.}
059. 
060.// 如果执行到这里,发生某项失败,则重新显示窗体
061.return View(model);
062.}
063. 
064.//
065.// GET: /Account/Register
066.[Authorize(Roles="Admin")]
067.public ActionResult Register()
068.{
069.return View();
070.}
071. 
072.//
073.// POST: /Account/Register
074.[HttpPost]
075.[Authorize(Roles = "Admin")]
076.[ValidateAntiForgeryToken]
077.public async Task<ActionResult> Register(RegisterViewModel model)
078.{
079.if (ModelState.IsValid)
080.{
081.var user = new ApplicationUser() {
082.UserName = model.UserName,
083.FirstName = model.FirstName,
084.LastName = model.LastName,
085.Email = model.Email,
086.};
087.var result = await UserManager.CreateAsync(user, model.Password);
088.if (result.Succeeded)
089.{
090.await SignInAsync(user, isPersistent: false);
091.return RedirectToAction("Index", "Home");
092.}
093.else
094.{
095.AddErrors(result);
096.}
097.}
098. 
099.// 如果执行到这里,发生某项失败,则重新显示窗体
100.return View(model);
101.}
102. 
103.//
104.// GEG: /Account/Index
105.[Authorize(Roles = "Admin")]
106.public ActionResult Index()
107.{
108.var Db = new ApplicationDbContext();
109.var users = Db.Users;
110.var model = new List<EditUserViewModel>();
111.foreach(var user in users)
112.{
113.var u = new EditUserViewModel(user);
114.model.Add(u);
115.}
116.return View(model);
117.}
118. 
119.//
120.// GEG: /Account/Edit
121.[Authorize(Roles = "Admin")]
122.public ActionResult Edit(string id , ManageMessageId? Message = null)
123.{
124.var Db = new ApplicationDbContext();
125.var user = Db.Users.First(u => u.UserName == id);
126.var model = new EditUserViewModel(user);
127.ViewBag.MessageId = Message;
128.return View(model);
129.}
130.//
131.// POST: /Account/Edit
132.[HttpPost]
133.[Authorize(Roles = "Admin")]
134.[ValidateAntiForgeryToken]
135.public async Task<ActionResult> Edit(EditUserViewModel model)
136.{
137.if (ModelState.IsValid)
138.{
139.var Db = new ApplicationDbContext();
140.var user = Db.Users.First(u => u.UserName == model.UserName);
141.user.FirstName = model.FirstName;
142.user.LastName = model.LastName;
143.user.Email = model.Email;
144.Db.Entry(user).State = System.Data.Entity.EntityState.Modified;
145.await Db.SaveChangesAsync();
146.return RedirectToAction("Index");
147.}
148.return View(model);
149.}
150. 
151.//
152.// GEG: /Account/Delete
153.[Authorize(Roles="Admin")]
154.public ActionResult Delete(string id = null)
155.{
156.var Db = new ApplicationDbContext();
157.var user = Db.Users.First(u => u.UserName == id);
158.var model = new EditUserViewModel(user);
159.return View(model);
160.}
161.//
162.// POST: /Account/Delete
163.[HttpPost, ActionName("Delete")]
164.[Authorize(Roles = "Admin")]
165.[ValidateAntiForgeryToken]
166.public ActionResult DeleteConfirmed(string id)
167.{
168.var Db = new ApplicationDbContext();
169.var user = Db.Users.First(u => u.UserName == id);
170.Db.Users.Remove(user);
171.Db.SaveChanges();
172.return RedirectToAction("Index");
173.}
174.//
175.// GEG: /Account/UserRoles
176.[Authorize(Roles = "Admin")]
177.public ActionResult UserRoles(string id)
178.{
179.var Db = new ApplicationDbContext();
180.var user = Db.Users.First(u => u.UserName == id);
181.var model = new SelectUserRolesViewModel(user);
182.return View(model);
183.}
184. 
185.//
186.// POST: /Account/UserRoles
187.[HttpPost]
188.[Authorize(Roles = "Admin")]
189.[ValidateAntiForgeryToken]
190.public ActionResult UserRoles(SelectUserRolesViewModel model)
191.{
192.if (ModelState.IsValid)
193.{
194.var idManager = new IdentityManager();
195.var Db = new ApplicationDbContext();
196.var user = Db.Users.First(u => u.UserName == model.UserName);
197.idManager.ClearUserRoles(user.Id);
198.foreach (var role in model.Roles)
199.{
200.if (role.Selected)
201.{
202.idManager.AddUserToRole(user.Id, role.RoleName);
203.}
204.}
205.return RedirectToAction("index");
206.}
207.return View();
208.}
209. 
210.//
211.// POST: /Account/Disassociate
212.[HttpPost]
213.[ValidateAntiForgeryToken]
214.public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
215.{
216.ManageMessageId? message = null;
217.IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
218.if (result.Succeeded)
219.{
220.message = ManageMessageId.RemoveLoginSuccess;
221.}
222.else
223.{
224.message = ManageMessageId.Error;
225.}
226.return RedirectToAction("Manage", new { Message = message });
227.}
228. 
229.//
230.// GET: /Account/Manage
231.public ActionResult Manage(ManageMessageId? message)
232.{
233.ViewBag.StatusMessage =
234.message == ManageMessageId.ChangePasswordSuccess ? "您的密码已变更。"
235.: message == ManageMessageId.SetPasswordSuccess ? "已设定您的密码。"
236.: message == ManageMessageId.RemoveLoginSuccess ? "已移除外部登入。"
237.: message == ManageMessageId.Error ? "发生错误。"
238.: "";
239.ViewBag.HasLocalPassword = HasPassword();
240.ViewBag.ReturnUrl = Url.Action("Manage");
241.return View();
242.}
243. 
244.//
245.// POST: /Account/Manage
246.[HttpPost]
247.[ValidateAntiForgeryToken]
248.public async Task<ActionResult> Manage(ManageUserViewModel model)
249.{
250.bool hasPassword = HasPassword();
251.ViewBag.HasLocalPassword = hasPassword;
252.ViewBag.ReturnUrl = Url.Action("Manage");
253.if (hasPassword)
254.{
255.if (ModelState.IsValid)
256.{
257.IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
258.if (result.Succeeded)
259.{
260.return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
261.}
262.else
263.{
264.AddErrors(result);
265.}
266.}
267.}
268.else
269.{
270.// User does not have a password so remove any validation errors caused by a missing OldPassword field
271.ModelState state = ModelState["OldPassword"];
272.if (state != null)
273.{
274.state.Errors.Clear();
275.}
276. 
277.if (ModelState.IsValid)
278.{
279.IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
280.if (result.Succeeded)
281.{
282.return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
283.}
284.else
285.{
286.AddErrors(result);
287.}
288.}
289.}
290. 
291.// 如果执行到这里,发生某项失败,则重新显示窗体
292.return View(model);
293.}
294. 
295.//
296.// POST: /Account/ExternalLogin
297.[HttpPost]
298.[AllowAnonymous]
299.[ValidateAntiForgeryToken]
300.public ActionResult ExternalLogin(string provider, string returnUrl)
301.{
302.// 要求重新导向至外部登入提供者
303.return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
304.}
305. 
306.//
307.// GET: /Account/ExternalLoginCallback
308.[AllowAnonymous]
309.public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
310.{
311.var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
312.if (loginInfo == null)
313.{
314.return RedirectToAction("Login");
315.}
316. 
317.// Sign in the user with this external login provider if the user already has a login
318.var user = await UserManager.FindAsync(loginInfo.Login);
319.if (user != null)
320.{
321.await SignInAsync(user, isPersistent: false);
322.return RedirectToLocal(returnUrl);
323.}
324.else
325.{
326.// If the user does not have an account, then prompt the user to create an account
327.ViewBag.ReturnUrl = returnUrl;
328.ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
329.return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName });
330.}
331.}
332. 
333.//
334.// POST: /Account/LinkLogin
335.[HttpPost]
336.[ValidateAntiForgeryToken]
337.public ActionResult LinkLogin(string provider)
338.{
339.// Request a redirect to the external login provider to link a login for the current user
340.return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
341.}
342. 
343.//
344.// GET: /Account/LinkLoginCallback
345.public async Task<ActionResult> LinkLoginCallback()
346.{
347.var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
348.if (loginInfo == null)
349.{
350.return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
351.}
352.var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
353.if (result.Succeeded)
354.{
355.return RedirectToAction("Manage");
356.}
357.return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
358.}
359. 
360.//
361.// POST: /Account/ExternalLoginConfirmation
362.[HttpPost]
363.[AllowAnonymous]
364.[ValidateAntiForgeryToken]
365.public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
366.{
367.if (User.Identity.IsAuthenticated)
368.{
369.return RedirectToAction("Manage");
370.}
371. 
372.if (ModelState.IsValid)
373.{
374.// 从外部登入提供者处取得使用者信息
375.var info = await AuthenticationManager.GetExternalLoginInfoAsync();
376.if (info == null)
377.{
378.return View("ExternalLoginFailure");
379.}
380.var user = new ApplicationUser() { UserName = model.UserName };
381.var result = await UserManager.CreateAsync(user);
382.if (result.Succeeded)
383.{
384.result = await UserManager.AddLoginAsync(user.Id, info.Login);
385.if (result.Succeeded)
386.{
387.await SignInAsync(user, isPersistent: false);
388.return RedirectToLocal(returnUrl);
389.}
390.}
391.AddErrors(result);
392.}
393. 
394.ViewBag.ReturnUrl = returnUrl;
395.return View(model);
396.}
397. 
398.//
399.// POST: /Account/LogOff
400.[HttpPost]
401.[ValidateAntiForgeryToken]
402.public ActionResult LogOff()
403.{
404.AuthenticationManager.SignOut();
405.return RedirectToAction("Index", "Home");
406.}
407. 
408.//
409.// GET: /Account/ExternalLoginFailure
410.[AllowAnonymous]
411.public ActionResult ExternalLoginFailure()
412.{
413.return View();
414.}
415. 
416.[ChildActionOnly]
417.public ActionResult RemoveAccountList()
418.{
419.var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId());
420.ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
421.return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
422.}
423. 
424.protected override void Dispose(bool disposing)
425.{
426.if (disposing && UserManager != null)
427.{
428.UserManager.Dispose();
429.UserManager = null;
430.}
431.base.Dispose(disposing);
432.}
433. 
434.#region Helper
435.// Used for XSRF protection when adding external logins
436.private const string XsrfKey = "XsrfId";
437. 
438.private IAuthenticationManager AuthenticationManager
439.{
440.get
441.{
442.return HttpContext.GetOwinContext().Authentication;
443.}
444.}
445. 
446.private async Task SignInAsync(ApplicationUser user, bool isPersistent)
447.{
448.AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
449.var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
450.AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
451.}
452. 
453.private void AddErrors(IdentityResult result)
454.{
455.foreach (var error in result.Errors)
456.{
457.ModelState.AddModelError("", error);
458.}
459.}
460. 
461.private bool HasPassword()
462.{
463.var user = UserManager.FindById(User.Identity.GetUserId());
464.if (user != null)
465.{
466.return user.PasswordHash != null;
467.}
468.return false;
469.}
470. 
471.public enum ManageMessageId
472.{
473.ChangePasswordSuccess,
474.SetPasswordSuccess,
475.RemoveLoginSuccess,
476.Error
477.}
478. 
479.private ActionResult RedirectToLocal(string returnUrl)
480.{
481.if (Url.IsLocalUrl(returnUrl))
482.{
483.return Redirect(returnUrl);
484.}
485.else
486.{
487.return RedirectToAction("Index", "Home");
488.}
489.}
490. 
491.private class ChallengeResult : HttpUnauthorizedResult
492.{
493.public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null)
494.{
495.}
496. 
497.public ChallengeResult(string provider, string redirectUri, string userId)
498.{
499.LoginProvider = provider;
500.RedirectUri = redirectUri;
501.UserId = userId;
502.}
503. 
504.public string LoginProvider { get; set; }
505.public string RedirectUri { get; set; }
506.public string UserId { get; set; }
507. 
508.public override void ExecuteResult(ControllerContext context)
509.{
510.var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
511.if (UserId != null)
512.{
513.properties.Dictionary[XsrfKey] = UserId;
514.}
515.context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
516.}
517.}
518.#endregion
519.}
520.}


 

修改相关 Views
 
修改Register.cshtml View 


01.@model RoleBaseProject.Models.RegisterViewModel
02.@{
03.ViewBag.Title = "注册";
04.}
05. 
06.<h2>@ViewBag.Title.</h2>
07. 
08.@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
09.{
10.@Html.AntiForgeryToken()
11.<h4>建立新的账户。</h4>
12.<hr />
13.@Html.ValidationSummary()
14.<div class="form-group">
15.@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
16.<div class="col-md-10">
17.@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
18.</div>
19.</div>
20.<div class="form-group">
21.@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
22.<div class="col-md-10">
23.@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
24.</div>
25.</div>
26.<div class="form-group">
27.@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
28.<div class="col-md-10">
29.@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
30.</div>
31.</div>
32.<div class="form-group">
33.@Html.LabelFor(m => m.FirstName, new { @class = "col-md-2 control-label" })
34.<div class="col-md-10">
35.@Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
36.</div>
37.</div>
38.<div class="form-group">
39.@Html.LabelFor(m => m.LastName, new { @class = "col-md-2 control-label" })
40.<div class="col-md-10">
41.@Html.TextBoxFor(m => m.LastName, new { @class = "form-control" })
42.</div>
43.</div>
44.<div class="form-group">
45.@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
46.<div class="col-md-10">
47.@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
48.</div>
49.</div>
50.<div class="form-group">
51.<div class="col-md-offset-2 col-md-10">
52.<input type="submit" class="btn btn-default" value="注册" />
53.</div>
54.</div>
55.}
56. 
57.@section Scripts {
58.@Scripts.Render("~/bundles/jqueryval")
59.}
 
新增Edit、Delete、Index 等方法的Views
 
新增Edit Views
 
在ASP.NET MVC5中建置以角色为基础的授权机制


在ASP.NET MVC5中建置以角色为基础的授权机制
 
用以下内容取代原程序


01.@model RoleBaseProject.Models.EditUserViewModel
02. 
03.@{
04.ViewBag.Title = "数据者数据编辑";
05.}
06. 
07.<h2>数据者数据编辑</h2>
08. 
09.@using (Html.BeginForm())
10.{
11.@Html.AntiForgeryToken()
12.<div class="form-horizontal">
13.<h4>修改原有账号数据</h4>
14.<hr />
15.@Html.ValidationSummary(true)
16.<div class="form-group">
17.@Html.LabelFor(model => model.UserName, new { @class = "control-label col-md-2" })
18.<div class="col-md-10">
19.@Html.EditorFor(model => model.UserName)
20.@Html.ValidationMessageFor(model => model.UserName)
21.</div>
22.</div>
23. 
24.<div class="form-group">
25.@Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" })
26.<div class="col-md-10">
27.@Html.EditorFor(model => model.FirstName)
28.@Html.ValidationMessageFor(model => model.FirstName)
29.</div>
30.</div>
31. 
32.<div class="form-group">
33.@Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" })
34.<div class="col-md-10">
35.@Html.EditorFor(model => model.LastName)
36.@Html.ValidationMessageFor(model => model.LastName)
37.</div>
38.</div>
39. 
40.<div class="form-group">
41.@Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" })
42.<div class="col-md-10">
43.@Html.EditorFor(model => model.Email)
44.@Html.ValidationMessageFor(model => model.Email)
45.</div>
46.</div>
47. 
48.<div class="form-group">
49.<div class="col-md-offset-2 col-md-10">
50.<input type="submit" value="存档" class="btn btn-default" />
51.</div>
52.</div>
53.</div>
54.}
55. 
56.<div>
57.@Html.ActionLink("回到使用者清单画面", "Index")
58.</div>
59. 
60.@section Scripts {
61.@Scripts.Render("~/bundles/jqueryval")
62.}

新增Delete的Views
 

在ASP.NET MVC5中建置以角色为基础的授权机制


在ASP.NET MVC5中建置以角色为基础的授权机制
 

用以下内容取代原程序


 

01.@model RoleBaseProject.Models.EditUserViewModel
02. 
03.<h3>使用者账号删除</h3>
04.<div>
05.<h4>确定要删除这个使用者账号?</h4>
06.<hr />
07.<dl class="dl-horizontal">
08.<dt>
09.@Html.DisplayNameFor(model => model.UserName)
10.</dt>
11. 
12.<dd>
13.@Html.DisplayFor(model => model.UserName)
14.</dd>
15. 
16.<dt>
17.@Html.DisplayNameFor(model => model.FirstName)
18.</dt>
19. 
20.<dd>
21.@Html.DisplayFor(model => model.FirstName)
22.</dd>
23. 
24.<dt>
25.@Html.DisplayNameFor(model => model.LastName)
26.</dt>
27. 
28.<dd>
29.@Html.DisplayFor(model => model.LastName)
30.</dd>
31. 
32.<dt>
33.@Html.DisplayNameFor(model => model.Email)
34.</dt>
35. 
36.<dd>
37.@Html.DisplayFor(model => model.Email)
38.</dd>
39. 
40.</dl>
41. 
42.@using (Html.BeginForm()) {
43.@Html.AntiForgeryToken()
44. 
45.<div class="form-actions no-color">
46.<input type="submit" value="删除" class="btn btn-default" /> |
47.@Html.ActionLink("回到使用者清单画面", "Index")
48.</div>
49.}
50.</div>

新增Index Views
 

在ASP.NET MVC5中建置以角色为基础的授权机制


在ASP.NET MVC5中建置以角色为基础的授权机制
 

用以下内容取代原程序


 

01.@model IEnumerable<RoleBaseProject.Models.EditUserViewModel>
02. 
03.@{
04.ViewBag.Title = "账号管理";
05.}
06. 
07.<h2>账号管理</h2>
08. 
09.<p>
10.@Html.ActionLink("新增账号", "Register")
11.</p>
12.<table class="table">
13.<tr>
14.<th>
15.@Html.DisplayNameFor(model => model.UserName)
16.</th>
17.<th>
18.@Html.DisplayNameFor(model => model.FirstName)
19.</th>
20.<th>
21.@Html.DisplayNameFor(model => model.LastName)
22.</th>
23.<th>
24.@Html.DisplayNameFor(model => model.Email)
25.</th>
26.<th></th>
27.</tr>
28. 
29.@foreach (var item in Model) {
30.<tr>
31.<td>
32.@Html.DisplayFor(modelItem => item.UserName)
33.</td>
34.<td>
35.@Html.DisplayFor(modelItem => item.FirstName)
36.</td>
37.<td>
38.@Html.DisplayFor(modelItem => item.LastName)
39.</td>
40.<td>
41.@Html.DisplayFor(modelItem => item.Email)
42.</td>
43.<td>
44.@Html.ActionLink("编辑", "Edit", new { id=item.UserName }) |
45.@Html.ActionLink("角色", "UserRoles", new { id=item.UserName }) |
46.@Html.ActionLink("删除", "Delete", new { id=item.UserName })
47.</td>
48.</tr>
49.}
50. 
51.</table>
新增好的三個View
 
 
在ASP.NET MVC5中建置以角色为基础的授权机制
 
新增UserRoles.cshtml View 并 新增程序代码
 
在ASP.NET MVC5中建置以角色为基础的授权机制


在ASP.NET MVC5中建置以角色为基础的授权机制
 
用以下内容取代原程序


01.@model RoleBaseProject.Models.SelectUserRolesViewModel
02.@{
03.ViewBag.Title = "使用者角色";
04.}
05. 
06.<h2>使用者角色 @Html.DisplayFor(model => model.UserName)</h2>
07.<hr />
08. 
09.@using (Html.BeginForm("UserRoles", "Account", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
10.{
11.@Html.AntiForgeryToken()
12. 
13.<div class="form-horizontal">
14.@Html.ValidationSummary(true)
15.<div class="form-group">
16.<div class="col-md-10">
17.@Html.HiddenFor(model => model.UserName)
18.</div>
19.</div>
20. 
21.<h4>选择要加入的角色</h4>
22.<br />
23.<hr />
24. 
25.<table>
26.<tr>
27.<th>
28.勾选
29.</th>
30.<th>
31.角色
32.</th>
33.</tr>
34.@Html.EditorFor(model => model.Roles)
35.</table>
36.<br />
37.<hr />
38. 
39.<div class="form-group">
40.<div class="col-md-offset-2 col-md-10">
41.<input type="submit" value="存档" class="btn btn-default" />
42.</div>
43.</div>
44.</div>
45.}


新增SelectRoleEditorViewModel.cshtml 在 Shared/EditorTemplates目录下
 

在ASP.NET MVC5中建置以角色为基础的授权机制


 

01.@model RoleBaseProject.Models.SelectRoleEditorViewModel
02.@Html.HiddenFor(model => model.RoleName)
03.<tr>
04.<td style="text-align:center">
05.@Html.CheckBoxFor(model => model.Selected)
06.</td>
07.<td>
08.@Html.DisplayFor(model => model.RoleName)
09.</td>
10.</tr>

在主页面上新增”账号管理" 功能按钮

在ASP.NET MVC5中建置以角色为基础的授权机制



移除主页面上的注册功能

 
 
在ASP.NET MVC5中建置以角色为基础的授权机制
 
启动 Migration功能
 
在ASP.NET MVC5中建置以角色为基础的授权机制
 
在Seed()方法中加入建立测试数据的程序代码
 
在ASP.NET MVC5中建置以角色为基础的授权机制
 
完整程序:


01.namespace RoleBaseProject.Migrations
02.{
03.using RoleBaseProject.Models;
04.using System;
05.using System.Data.Entity;
06.using System.Data.Entity.Migrations;
07.using System.Linq;
08. 
09.internal sealed class Configuration : DbMigrationsConfiguration<RoleBaseProject.Models.ApplicationDbContext>
10.{
11.public Configuration()
12.{
13.AutomaticMigrationsEnabled = false;
14.}
15. 
16.protected override void Seed(RoleBaseProject.Models.ApplicationDbContext context)
17.{
18.// This method will be called after migrating to the latest version.
19. 
20.// You can use the DbSet<T>.AddOrUpdate() helper extension method
21.// to avoid creating duplicate seed data. E.g.
22.//
23.// context.People.AddOrUpdate(
24.// p => p.FullName,
25.// new Person { FullName = "Andrew Peters" },
26.// new Person { FullName = "Brice Lambson" },
27.// new Person { FullName = "Rowan Miller" }
28.// );
29.//
30.this.AddUserAndRoles();
31.}
32. 
33.bool AddUserAndRoles()
34.{
35.bool success = false;
36. 
37.var idManager = new IdentityManager();
38.success = idManager.CreateRole("Admin");
39.if (!success == true) return success;
40. 
41.success = idManager.CreateRole("CanEdit");
42.if (!success == true) return success;
43. 
44.success = idManager.CreateRole("User");
45.if (!success) return success;
46. 
47.var newUser = new ApplicationUser()
48.{
49.UserName = "jatten",
50.FirstName = "John",
51.LastName = "Atten",
52.Email = "jatten@typecastexception.com"
53.};
54. 
55.success = idManager.CreateUser(newUser, "Password1");
56.if (!success) return success;
57. 
58.success = idManager.AddUserToRole(newUser.Id, "Admin");
59.if (!success) return success;
60. 
61.success = idManager.AddUserToRole(newUser.Id, "CanEdit");
62.if (!success) return success;
63. 
64.success = idManager.AddUserToRole(newUser.Id, "User");
65.if (!success) return success;
66. 
67.return success;
68.}
69.}
70.}


更新数据库
 

在ASP.NET MVC5中建置以角色为基础的授权机制


在ASP.NET MVC5中建置以角色为基础的授权机制
 

执行结果
 

在ASP.NET MVC5中建置以角色为基础的授权机制
 

以具有Admin角色的使用者登入后执行账户管理功能:
 

在ASP.NET MVC5中建置以角色为基础的授权机制