65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.30/5 (21投票s)

2015 年 5 月 22 日

CPOL

2分钟阅读

viewsIcon

204947

downloadIcon

10102

在 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 帐户和实现基于角色的身份验证。因此,如果您发现代码和标题相似,那不是巧合。 :)

直奔主题

我还没有成为一个有魅力的作家。所以让我们直奔主题。

创建项目

这是最简单的部分。

  1. 转到“文件”>>“新建项目”
  2. 选择“Web”>>“ASP.NET Web 应用程序”
  3. 输入项目名称:ASPNetMVCExtendingIdentity2Roles
  4. 单击“确定”。
  5. 选择 MVC 作为模板。
  6. 单击“确定”。

万岁!!!您已完成项目创建。现在,如果您看过本文附带的代码,您可能没有注意到我对文件和文件夹进行了一些调整。但我可以肯定,如果出现构建错误,您会知道该怎么做。

域模型

模型是 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 链接。感谢您阅读至今。

© . All rights reserved.