ASP.NET MVC 5:扩展 ASP.NET Identity 2.0 角色和基于角色的授权实现






4.30/5 (21投票s)
在 ASP.NET MVC5 中使用 ASP.NET Identity 2.0 实现基于角色的基本授权
引言
许多人已经发现 ASP.NET Identity 2.0 不能像 Identity 1.0 那样使用相同的代码,就像我一样。在本文中,我尝试使用 ASP.NET MVC 5 和 Identity 2.0 实现一个简单的基于角色的授权。
免责声明
我使用的大部分代码都取自 John Atten 的精彩文章 在 ASP.NET MVC 5 中扩展 Identity 帐户和实现基于角色的身份验证。因此,如果您发现代码和标题相似,那不是巧合。 :)
直奔主题
我还没有成为一个有魅力的作家。所以让我们直奔主题。
创建项目
这是最简单的部分。
- 转到“文件”>>“新建项目”
- 选择“Web”>>“ASP.NET Web 应用程序”
- 输入项目名称:ASPNetMVCExtendingIdentity2Roles
- 单击“确定”。
- 选择 MVC 作为模板。
- 单击“确定”。
万岁!!!您已完成项目创建。现在,如果您看过本文附带的代码,您可能没有注意到我对文件和文件夹进行了一些调整。但我可以肯定,如果出现构建错误,您会知道该怎么做。
域模型
模型是 MVC 中最重要的东西。我将从域模型开始。
ApplicationRole
在 Models 文件夹中创建一个新类,并将其命名为 ApplicationRole
。将以下代码放入文件中。
using Microsoft.AspNet.Identity.EntityFramework;
namespace ASPNetMVCExtendingIdentity2Roles.Models
{
public class ApplicationRole : IdentityRole
{
public ApplicationRole() : base() { }
public ApplicationRole(string name, string description)
: base(name)
{
this.Description = description;
}
public virtual string Description { get; set; }
}
}
ApplicationUserRole
是时候创建另一个类了,这次是 ApplicationUserRole
,它将 ApplicationUser
关联到 ApplicationRole
。
using Microsoft.AspNet.Identity.EntityFramework;
namespace ASPNetMVCExtendingIdentity2Roles.Models
{
public class ApplicationUserRole : IdentityUserRole
{
public ApplicationUserRole()
: base()
{ }
public ApplicationRole Role { get; set; }
}
}
ApplicationUser
我们快到了。我们需要对已经存在的 ApplicationUser
类进行一些修饰。使用以下代码修改该类。
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
namespace ASPNetMVCExtendingIdentity2Roles.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
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
}
DbContext
啊哈!!!我们已经到了本文最激动人心的部分,DbContext。DbContext 是将您的应用程序粘合到数据库的粘合剂。应该好好照顾它。我的一个有点笨拙。你为什么不试着让你的更漂亮呢?我在此类中包含我的 DbContext 初始化器、我的种子机制以及一些业务功能,这绝对不是一个好的做法。但是我们来这里是为了解决 Identity 问题,而不是为了学习软件开发最佳实践,对吗?所以,这是我的代码。
using ASPNetMVCExtendingIdentity2Roles.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace ASPNetMVCExtendingIdentity2Roles.Context
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<ApplicationRole> Roles { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
if (modelBuilder == null)
{
throw new ArgumentNullException("ModelBuilder is NULL");
}
base.OnModelCreating(modelBuilder);
//Defining the keys and relations
modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers");
modelBuilder.Entity<ApplicationRole>().HasKey<string>(r => r.Id).ToTable("AspNetRoles");
modelBuilder.Entity<ApplicationUser>().HasMany<ApplicationUserRole>((ApplicationUser u) => u.UserRoles);
modelBuilder.Entity<ApplicationUserRole>().HasKey(r => new { UserId = r.UserId, RoleId = r.RoleId }).ToTable("AspNetUserRoles");
}
public bool Seed(ApplicationDbContext context)
{
#if DEBUG
bool success = false;
ApplicationRoleManager _roleManager = new ApplicationRoleManager(new RoleStore<ApplicationRole>(context));
success = this.CreateRole(_roleManager, "Admin", "Global Access");
if (!success == true) return success;
success = this.CreateRole(_roleManager, "CanEdit", "Edit existing records");
if (!success == true) return success;
success = this.CreateRole(_roleManager, "User", "Restricted to business domain activity");
if (!success) return success;
// Create my debug (testing) objects here
ApplicationUserManager userManager = new ApplicationUserManager(new UserStore<ApplicationUser>(context));
ApplicationUser user = new ApplicationUser();
PasswordHasher passwordHasher = new PasswordHasher();
user.UserName = "youremail@testemail.com";
user.Email = "youremail@testemail.com";
IdentityResult result = userManager.Create(user, "Pass@123");
success = this.AddUserToRole(userManager, user.Id, "Admin");
if (!success) return success;
success = this.AddUserToRole(userManager, user.Id, "CanEdit");
if (!success) return success;
success = this.AddUserToRole(userManager, user.Id, "User");
if (!success) return success;
return success;
#endif
}
public bool RoleExists(ApplicationRoleManager roleManager, string name)
{
return roleManager.RoleExists(name);
}
public bool CreateRole(ApplicationRoleManager _roleManager, string name, string description = "")
{
var idResult = _roleManager.Create<ApplicationRole, string>(new ApplicationRole(name, description));
return idResult.Succeeded;
}
public bool AddUserToRole(ApplicationUserManager _userManager, string userId, string roleName)
{
var idResult = _userManager.AddToRole(userId, roleName);
return idResult.Succeeded;
}
public void ClearUserRoles(ApplicationUserManager userManager, string userId)
{
var user = userManager.FindById(userId);
var currentRoles = new List<IdentityUserRole>();
currentRoles.AddRange(user.UserRoles);
foreach (ApplicationUserRole role in currentRoles)
{
userManager.RemoveFromRole(userId, role.Role.Name);
}
}
public void RemoveFromRole(ApplicationUserManager userManager, string userId, string roleName)
{
userManager.RemoveFromRole(userId, roleName);
}
public void DeleteRole(ApplicationDbContext context, ApplicationUserManager userManager, string roleId)
{
var roleUsers = context.Users.Where(u => u.UserRoles.Any(r => r.RoleId == roleId));
var role = context.Roles.Find(roleId);
foreach (var user in roleUsers)
{
this.RemoveFromRole(userManager, user.Id, role.Name);
}
context.Roles.Remove(role);
context.SaveChanges();
}
/// <summary>
/// Context Initializer
/// </summary>
public class DropCreateAlwaysInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
context.Seed(context);
base.Seed(context);
}
}
}
}
不要忘记更改种子部分的用户名和密码。要初始化 DbContext,需要更新 Global.asax 文件。
using ASPNetMVCExtendingIdentity2Roles.Context;
using System.Data.Entity;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace ASPNetMVCExtendingIdentity2Roles
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
#if DEBUG
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbContext.DropCreateAlwaysInitializer());
#endif
}
}
}
ViewModels
ViewModel 是一个包含视图所需数据的类。它们不是 MVC 模式的强制部分,但它们使实现提供所需数据的视图变得容易。
RoleViewModels
using System.ComponentModel.DataAnnotations;
namespace ASPNetMVCExtendingIdentity2Roles.Models
{
public class RoleViewModel
{
public string RoleName { get; set; }
public string Description { get; set; }
public RoleViewModel() { }
public RoleViewModel(ApplicationRole role)
{
this.RoleName = role.Name;
this.Description = role.Description;
}
}
public class SelectRoleEditorViewModel
{
public SelectRoleEditorViewModel() { }
// Update this to accept an argument of type ApplicationRole:
public SelectRoleEditorViewModel(ApplicationRole role)
{
this.RoleName = role.Name;
// Assign the new Descrption property:
this.Description = role.Description;
}
public bool Selected { get; set; }
[Required]
public string RoleName { get; set; }
// Add the new Description property:
public string Description { get; set; }
}
public class EditRoleViewModel
{
public string OriginalRoleName { get; set; }
public string RoleName { get; set; }
public string Description { get; set; }
public EditRoleViewModel() { }
public EditRoleViewModel(ApplicationRole role)
{
this.OriginalRoleName = role.Name;
this.RoleName = role.Name;
this.Description = role.Description;
}
}
}
控制器
RolesController
创建 RolesController
并放入以下代码。
using ASPNetMVCExtendingIdentity2Roles.Context;
using ASPNetMVCExtendingIdentity2Roles.Models;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web.Mvc;
namespace ASPNetMVCExtendingIdentity2Roles.Controllers
{
public class RolesController : Controller
{
private ApplicationDbContext _db = new ApplicationDbContext();
public ActionResult Index()
{
var rolesList = new List<RoleViewModel>();
foreach (var role in _db.Roles)
{
var roleModel = new RoleViewModel(role);
rolesList.Add(roleModel);
}
return View(rolesList);
}
[Authorize(Roles = "Admin")]
public ActionResult Create(string message = "")
{
ViewBag.Message = message;
return View();
}
[HttpPost]
[Authorize(Roles = "Admin")]
public ActionResult Create([Bind(Include =
"RoleName,Description")]RoleViewModel model)
{
string message = "That role name has already been used";
if (ModelState.IsValid)
{
var role = new ApplicationRole(model.RoleName, model.Description);
ApplicationRoleManager _roleManager = new ApplicationRoleManager(new RoleStore<ApplicationRole>(_db));
if (_db.RoleExists(_roleManager, model.RoleName))
{
return View(message);
}
else
{
_db.CreateRole(_roleManager, model.RoleName, model.Description);
return RedirectToAction("Index", "Roles");
}
}
return View();
}
[Authorize(Roles = "Admin")]
public ActionResult Edit(string id)
{
// It's actually the Role.Name tucked into the id param:
var role = _db.Roles.First(r => r.Name == id);
var roleModel = new EditRoleViewModel(role);
return View(roleModel);
}
[HttpPost]
[Authorize(Roles = "Admin")]
public ActionResult Edit([Bind(Include =
"RoleName,OriginalRoleName,Description")] EditRoleViewModel model)
{
if (ModelState.IsValid)
{
var role = _db.Roles.First(r => r.Name == model.OriginalRoleName);
role.Name = model.RoleName;
role.Description = model.Description;
_db.Entry(role).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
[Authorize(Roles = "Admin")]
public ActionResult Delete(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var role = _db.Roles.First(r => r.Name == id);
var model = new RoleViewModel(role);
if (role == null)
{
return HttpNotFound();
}
return View(model);
}
[Authorize(Roles = "Admin")]
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(string id)
{
var role = _db.Roles.First(r => r.Name == id);
ApplicationUserManager userManager = new ApplicationUserManager(new UserStore<ApplicationUser>(_db));
_db.DeleteRole(_db, userManager, role.Id);
return RedirectToAction("Index");
}
}
}
视图
右键单击 RolesController
中的 Actions,然后创建相应的视图。
Create
@model ASPNetMVCExtendingIdentity2Roles.Models.RoleViewModel
@{
ViewBag.Title = "Create";
}
<h2>Create Role</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>RolesViewModel</h4>
<hr />
@Html.ValidationSummary(true)
@if (ViewBag.Message != "")
{
<p style="color: red">ViewBag.Message</p>
}
<div class="form-group">
@Html.LabelFor(model => model.RoleName, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.RoleName)
@Html.ValidationMessageFor(model => model.RoleName)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Description, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
删除
@model ASPNetMVCExtendingIdentity2Roles.Models.RoleViewModel
@{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>RolesViewModel</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.RoleName)
</dt>
<dd>
@Html.DisplayFor(model => model.RoleName)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Description)
</dt>
<dd>
@Html.DisplayFor(model => model.Description)
</dd>
</dl>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-default" /> |
@Html.ActionLink("Back to List", "Index")
</div>
}
</div>
未使用。
@model ASPNetMVCExtendingIdentity2Roles.Models.EditRoleViewModel
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>EditRolesViewModel</h4>
<hr />
@Html.ValidationSummary(true)
@*Hide the original name away for later:*@
@Html.HiddenFor(model => model.OriginalRoleName)
<div class="form-group">
@Html.LabelFor(model => model.RoleName, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.RoleName)
@Html.ValidationMessageFor(model => model.RoleName)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Description, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
目录
@model IEnumerable<ASPNetMVCExtendingIdentity2Roles.Models.RoleViewModel>
@{
ViewBag.Title = "Application Roles";
}
<h2>Application Roles</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.RoleName)
</th>
<th>
@Html.DisplayNameFor(model => model.Description)
</th>
<th></th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.RoleName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Description)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.RoleName }) |
@Html.ActionLink("Delete", "Delete", new { id = item.RoleName })
</td>
</tr>
}
</table>
结论
就是这些。运行您的应用程序。使用您在种子中使用的用户名和密码登录,点击 Roles 链接。感谢您阅读至今。