ASP.NET Identity教程三:(用户授权1)
.1. 访问授权
在上面详细讲解了与用户相关的操作,当用户登录到网站系统中之后,我们要对用户进行控制,并不是所有的页面都可以访问,只有部分页面,或具有相应的权限之后才可以访问。这里我们只对用户进行相关授权,后面还会讲到角色。在 ASP.NET MVC 中,对页面的授权,是针对控制器或控制器中的方法授权的。授权是使用 Authorize 属性执行的,Authorize指定对控制器或操作方法的访问只限于满足授权要求的用户。
对于控制器,其中会包括许多的操作方法,如果对整个控制器进行授权,则该控制器下的所有操作方法都与控制器具有一样的权限。
我们在Home控制器的index方法上进行增加授权
namespace jsdhh2.Controllers { public class HomeController : Controller { [Authorize] public ActionResult Index() { return View(); }
当控制器使用了 Authorize 特性之后,只有登录用户才能访问index视图。
我们再换个方法
namespace jsdhh2.Controllers { [Authorize] public class HomeController : Controller { public ActionResult Index() { return View(); }
只有登录过的用户才可以访问该控制器下的所有方法。
测试一下
发现只要一进入HOME,都会要求登录,同时转入到User/Login
原因是由于我们在“App_Start”文件夹中的“Startup.Auth.cs”文件中配置了当访问到没有权限的页面所转向的 URL
p.CreatePerOwinContext<SignInManager>(SignInManager.Create); //注册RoleManager管理器 //app.CreatePerOwinContext<RoleManager>(RoleManager.Create); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/User/Login") });
配置的是登录的 URL,也就是当认证没有通过时,直接转到登录页
面,也就是图 7-46 的登录页面,但在 URL 上有 ReturnUrl 参数,表示登录成功后
会转到之前访问的页面。
当登录成功后,就会转到之前访问的页面,这就是对控制器授权的结果。当然也可以对控制器中的操作方法进行授权。
控制器中有 2 个方法,其中对 Index()方法使用了Authorize,表示必须登录之后才可能访问。而 Home()方法使用了 AllowAnonymous,表示可以匿名访问,没有登录也可以访问。
.2. 限定用户
对于控制器下的某个指定的操作方法,我们可以限定一些用户,指定只有某几个用户访问,使用 Authorize 特性的 Users 属性即可实现,多个用户之间使用英文的逗号(,)隔开即可。
[Authorize(Users ="abc@yidosoft.cn,yidosoft@163.com")] public ActionResult Index() { return View(); } [AllowAnonymous] public ActionResult Home() { return View(); }
对于Index()方法,只有 abc@yidosoft.cn,yidosoft@163.com 这2 个用户才能访问
3. 角色管理
在上面我们对用户管理进行了详细的讲解,使用用户也可以授权,但是需要固定用户,这在系统中是不方便的。一般都会使用角色来授权,只需要将指定的用户添加到角色中即可,对外的权限都是由角色来完成的。
1. 配置 RoleManager
这里我们在之前的 UserManger 的基础上配置 RoleManager 管理器。用来管理角色。
1. 继承 IdentityRole
对于角色管理,与用户管理是一样的,也是从继承 ASP.NET Identity 相关的基类或接口开始的,这样做的好处是可以进行扩展,这里也是像 User 一样,继承IdentityUser,只不过,Role 要继承 IdentityRole 类。
项目的“Models”文件夹中添加一个名称为“Role”的类,并继承
IdentityRole 基类。
using Microsoft.AspNet.Identity.EntityFramework; namespace Yidosoft.Identity.Models { public class Role : IdentityRole { public Role() : base() { } public Role(string name) : base(name) { } //扩展属性 } }
默认情况下,IdentityRole 中可用的属性有 3 个
对于 Id 属性,映射到数据库中就是主键,Name 是指角色的名称,而 Users 属性是一个集合属性,可以获取该属性下所有关联的用户。
在数据库中,存储角色信息的表是“AspNetRoles”,如图 8-3 所示:
默认只有 Id 和 Name 字段,当然我们也可以扩展自己的字段,只需要在 Role 中编写属性即可,与 User 扩展属性是一样的。如下代码:
sing Microsoft.AspNet.Identity.EntityFramework; namespace Yidosoft.Identity.Models { public class Role : IdentityRole { public Role() : base() { } public Role(string name) : base(name) { } //扩展属性 } }
我们这里就不扩展了,直接使用默认自带的属性。
1.2. 继承 RoleManager<Role>
继承 RoleManager<Role>与继承 UserManager<User>是一样的,提供了与数据库的持久化数据。在“App_Start”文件夹的“IdentityConfig.cs”文件中编写如下代码
// <summary> // 配置角色管理器 // </summary> public class RoleManager : RoleManager<Role> { public RoleManager(RoleStore<Role> store) : base(store) { } public static RoleManager Create(IdentityFactoryOptions<RoleManager> options, IOwinContext context) { var manager = new RoleManager(new RoleStore<Role>(context.Get<DAL.IdentityDbContext>())); return manager; } }
配置方法与 UserManager 是一样的
3. 注册 RoleManager
最后还需要在 OWIN Startup 类中注册一下,注册之后,就可以在每次的请求中使用了。存储在 OWIN 上下文环境字典中。
打开“App_Start”文件夹中的“Startup.Auth.cs”文件,添加如下代码:
app.CreatePerOwinContext<RoleManager>(RoleManager.Create);
整个完整 的startup.Auth.CS代码如下
using System; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.Google; using Owin; using jsdhh2.Models; using jsdhh2.DAL; namespace jsdhh2 { public partial class Startup { // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864 public void ConfigureAuth(IAppBuilder app) { // 配置数据库上下文、用户管理器和登录管理器,以便为每个请求使用单个实例 //app.CreatePerOwinContext(ApplicationDbContext.Create); //app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); //app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); app.CreatePerOwinContext(IdentityDbContext.Create); //注册UserManger管理器 app.CreatePerOwinContext<UserManager>(UserManager.Create); //注册SignInManager管理器 app.CreatePerOwinContext<SignInManager>(SignInManager.Create); //注册RoleManager管理器 //app.CreatePerOwinContext<RoleManager>(RoleManager.Create); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/User/Login") }); //// 使应用程序可以使用 Cookie 来存储已登录用户的信息 //// 并使用 Cookie 来临时存储有关使用第三方登录提供程序登录的用户的信息 //// 配置登录 Cookie //app.UseCookieAuthentication(new CookieAuthenticationOptions // { // AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, // LoginPath = new PathString("/Account/Login"), // Provider = new CookieAuthenticationProvider // { // // 当用户登录时使应用程序可以验证安全戳。 // // 这是一项安全功能,当你更改密码或者向帐户添加外部登录名时,将使用此功能。 // OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( // validateInterval: TimeSpan.FromMinutes(30), // regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) // } // }); // app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); // // 使应用程序可以在双重身份验证过程中验证第二因素时暂时存储用户信息。 // app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5)); // // 使应用程序可以记住第二登录验证因素,例如电话或电子邮件。 // // 选中此选项后,登录过程中执行的第二个验证步骤将保存到你登录时所在的设备上。 // // 此选项类似于在登录时提供的“记住我”选项。 // app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); // // 取消注释以下行可允许使用第三方登录提供程序登录 // //app.UseMicrosoftAccountAuthentication( // // clientId: "", // // clientSecret: ""); // //app.UseTwitterAuthentication( // // consumerKey: "", // // consumerSecret: ""); // //app.UseFacebookAuthentication( // // appId: "", // // appSecret: ""); // //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions() // //{ // // ClientId = "", // // ClientSecret = "" // //}); } } }
目前为止,我们已经注册了 3 个管理器,只有在这里注册之后,才可以在控制器中使用。
2. 创建控制器
这里创建一个名称为“RoleController”控制器来讲解与角色相关的操作。
然后在该控制器中使用HttpContext.GetOwinContext().Get()方法获取RoleManager管理器。如下代码:
public class RoleController : Controller { private RoleManager _roleManager; public RoleManager RoleManager { get { return _roleManager ?? HttpContext.GetOwinContext().Get<RoleManager>(); } private set { _roleManager = value; } } private UserManager _userManager; public UserManager UserManager { get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<UserManager>(); } private set { _userManager = value; } }
在该控制器中,使用 RoleManager 中相应的方法就可以操作角色了。
为了方便我把所有的代码都放在下面,其中有二处比如增加角色等,要和模型关联,暂时先注释了
using Microsoft.AspNet.Identity.Owin; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using jsdhh2.ViewModels; using System.Threading.Tasks; using Microsoft.AspNet.Identity; namespace jsdhh2.Controllers { public class RoleController : Controller { private RoleManager _roleManager; public RoleManager RoleManager { get { return _roleManager ?? HttpContext.GetOwinContext().Get<RoleManager>(); } private set { _roleManager = value; } } private UserManager _userManager; public UserManager UserManager { get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<UserManager>(); } private set { _userManager = value; } } // GET: Role public ActionResult Index() { return View(); } /// <summary> /// 添加角色 /// </summary> /// <returns></returns> [HttpGet] public ActionResult Add() { return View(); } ///// <summary> ///// 添加角色 ///// </summary> ///// <param name="model"></param> ///// <returns></returns> //[HttpPost] //[ValidateAntiForgeryToken] //public async Task<ActionResult> Add(AddRoleViewModel model) //{ // if (ModelState.IsValid) // { // var result = await RoleManager.CreateAsync(new Models.Role(model.Name)); // if (result.Succeeded) // { // return RedirectToAction("List"); // } // AddErrors(result); // } // return View(); //} /// <summary> /// 所有角色名称 /// </summary> /// <returns></returns> [HttpGet] public ActionResult List() { var roleList = RoleManager.Roles.ToList(); return View(roleList); } ///// <summary> ///// 编辑角色 ///// </summary> ///// <returns></returns> //[HttpGet] //public ActionResult Edit(string id) //{ // if (string.IsNullOrEmpty(id)) // { // return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); // } // Models.Role role = RoleManager.FindById(id); // if (role == null) // { // return HttpNotFound(); // } // var editModel = new EditRoleViewModel() // { // Name = role.Name // }; // return View(editModel); //} ///// <summary> ///// 编辑角色 ///// </summary> ///// <param name="id"></param> ///// <param name="model"></param> ///// <returns></returns> //[HttpPost] //[ValidateAntiForgeryToken] //public async Task<ActionResult> Edit(string id, EditRoleViewModel model) //{ // if (ModelState.IsValid && !string.IsNullOrEmpty(id)) // { // var role = RoleManager.FindById(id); // role.Name = model.Name; // var result = await RoleManager.UpdateAsync(role); // if (result.Succeeded) // { // return RedirectToAction("List"); // } // AddErrors(result); // } // return View(model); //} /// <summary> /// 删除角色 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpGet] public async Task<ActionResult> Del(string id) { if (string.IsNullOrEmpty(id)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } var role = RoleManager.FindById(id); if (role == null) { return HttpNotFound(); } var result = await RoleManager.DeleteAsync(role); if (!result.Succeeded) { AddErrors(result); } return RedirectToAction("List"); } /// <summary> /// 用户到角色列表 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpGet] public ActionResult UserToRole(string id) { if (string.IsNullOrWhiteSpace(id)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } var role = RoleManager.FindById(id); ViewBag.RoleName = role.Name; ViewBag.RoleId = id; if (role == null) { return HttpNotFound(); } var memberIDs = role.Users.Select(x => x.UserId).ToArray(); var members = UserManager.Users.Where(x => memberIDs.Any(y => y == x.Id)); var membersNo = UserManager.Users.Except(members); return View(membersNo); } /// <summary> /// 添加用户到角色 /// </summary> /// <param name="userId"></param> /// <param name="roleName"></param> /// <param name="roleId"></param> /// <returns></returns> public ActionResult AddToRole(string userId, string roleName, string roleId) { if (!string.IsNullOrWhiteSpace(userId) && !string.IsNullOrWhiteSpace(roleName)) { UserManager.AddToRole(userId, roleName); } return RedirectToAction("UserToRole", new { id = roleId }); } /// <summary> /// 查看角色中的用户 /// </summary> /// <param name="id"></param> /// <returns></returns> public ActionResult ViewRoleUser(string id) { if (string.IsNullOrWhiteSpace(id)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } var role = RoleManager.FindById(id); ViewBag.RoleName = role.Name; var memberIDs = role.Users.Select(x => x.UserId).ToArray(); var members = UserManager.Users.Where(x => memberIDs.Any(y => y == x.Id)); return View(members); } /// <summary> /// 移除用户 /// </summary> /// <param name="roleName"></param> /// <param name="userId"></param> /// <returns></returns> [HttpGet] public ActionResult RemoveRoleUser(string roleName, string userId) { if (string.IsNullOrWhiteSpace(roleName) && string.IsNullOrWhiteSpace(userId)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } var result = UserManager.RemoveFromRole(userId, roleName); var role = RoleManager.FindByName(roleName); if (!result.Succeeded) { return View("Error"); } return RedirectToAction("ViewRoleUser", new { id = role.Id }); } private void AddErrors(IdentityResult result) { foreach (var error in result.Errors) { ModelState.AddModelError("", error); } } } }