在asp.net core中,微软提供了基于认证(Authentication)和授权(Authorization)的方式,来实现权限管理的,本篇博文,介绍基于固定角色的权限管理和自定义角色权限管理,本文内容,更适合传统行业的BS应用,而非互联网应用。
在asp.net core中,我们认证(Authentication)通常是在Login的Post Action中进行用户名或密码来验证用户是否正确,如果通过验证,即该用户就会获得一个或几个特定的角色,通过ClaimTypes.Role来存储角色,从而当一个请求到达时,用这个角色和Controller或Action上加的特性[Authorize(Roles="admin,system")]来授权是否有权访问该Action。本文中的自定义角色,会把验证放在中间件中进行处理。
一、固定角色:
即把角色与具体的Controller或Action直接关联起来,整个系统中的角色是固定的,每种角色可以访问那些Controller或Action也是固定的,这做法比较适合小型项目,角色分工非常明确的项目。
项目代码:
https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/RolePrivilegeManagement
始于startup.cs
需要在ConfigureServices中注入Cookie的相关信息,options是CookieAuthenticationOptions,关于这个类型提供如下属性,可参考:https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?tabs=aspnetcore2x
它提供了登录的一些信息,或登录生成Cookie的一些信息,用以后
1 ????????public void ConfigureServices(IServiceCollection services) 2 ????????{ 3 ????????????services.AddMvc(); 4 ????????????//添加认证Cookie信息 5 ????????????services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) 6 ?????????????.AddCookie(options => 7 ?????????????{ 8 ?????????????????options.LoginPath = new PathString("/login"); 9 ?????????????????options.AccessDeniedPath = new PathString("/denied");10 ?????????????});11 ????????}12 13 ????????public void Configure(IApplicationBuilder app, IHostingEnvironment env)14 ????????{15 ????????????if (env.IsDevelopment())16 ????????????{17 ????????????????app.UseDeveloperExceptionPage();18 ????????????????app.UseBrowserLink();19 ????????????}20 ????????????else21 ????????????{22 ????????????????app.UseExceptionHandler("/Home/Error");23 ????????????}24 ????????????app.UseStaticFiles();25 ????????????//验证中间件26 ????????????app.UseAuthentication();27 ????????????app.UseMvc(routes =>28 ????????????{29 ????????????????routes.MapRoute(30 ????????????????????name: "default",31 ????????????????????template: "{controller=Home}/{action=Index}/{id?}");32 ????????????});33 ????????}
HomeController.cs
对于Login Get的Action,把returnUrl用户想要访问的地址(有可能用户记录下想要访问的url了,但系统会转到登录页,登录成功后直接跳转到想要访问的returnUrl页)
对于Login Post的Action,验证用户密和密码,成功能,定义一个ClaimsIdentity,把用户名和角色,和用户姓名的声明都添回进来(这个角色,就是用来验证可访问action的角色 )作来该用户标识,接下来调用HttpContext.SignInAsync进行登录,注意此方法的第一个参数,必需与StartUp.cs中services.AddAuthentication的参数相同,AddAuthentication是设置登录,SigninAsync是按设置参数进行登录
对于Logout Get的Action,是退出登录
HomeController上的[Authorize(Roles=”admin,system”)]角色和权限的关系时,所有Action只有admin和system两个角色能访问到,About上的[Authorize(Roles=”admin”)]声明这个action只能admin角色访问,Contact上的[Authorize(Roles=”system”)]声明这个action只能system角色访问,如果action上声明的是[AllowAnomymous],说明不受授权管理,可以直接访问。
1 using System; 2 using System.Collections.Generic; 3 using System.Diagnostics; 4 using System.Linq; 5 using System.Threading.Tasks; 6 using Microsoft.AspNetCore.Mvc; 7 using RolePrivilegeManagement.Models; 8 using System.Security.Claims; 9 using Microsoft.AspNetCore.Authentication;10 using Microsoft.AspNetCore.Authentication.Cookies;11 using Microsoft.AspNetCore.Authorization;12 13 namespace RolePrivilegeManagement.Controllers14 {15 ????[Authorize(Roles = "admin,system")]16 ????public class HomeController : Controller17 ????{18 ????????public IActionResult Index()19 ????????{20 ????????????return View();21 ????????}22 ????????[Authorize(Roles = "admin")]23 ????????public IActionResult About()24 ????????{25 ????????????ViewData["Message"] = "Your application description page.";26 ????????????return View();27 ????????}28 ????????[Authorize(Roles = "system")]29 ????????public IActionResult Contact()30 ????????{31 ????????????ViewData["Message"] = "Your contact page.";32 ????????????return View();33 ????????}34 ????????public IActionResult Error()35 ????????{36 ????????????return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });37 ????????}38 ????????[AllowAnonymous]39 ????????[HttpGet("login")]40 ????????public IActionResult Login(string returnUrl = null)41 ????????{42 ????????????TempData["returnUrl"] = returnUrl;43 ????????????return View();44 ????????}45 ????????[AllowAnonymous]46 ????????[HttpPost("login")]47 ????????public async Task<IActionResult> Login(string userName, string password, string returnUrl = null)48 ????????{49 ????????????var list = new List<dynamic> {50 ????????????????new { UserName = "gsw", Password = "111111", Role = "admin" },51 ????????????????new { UserName = "aaa", Password = "222222", Role = "system" }52 ????????????};53 ????????????var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password);54 ????????????if (user!=null)55 ????????????{56 ????????????????//用户标识57 ????????????????var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);58 ????????????????identity.AddClaim(new Claim(ClaimTypes.Sid, userName));59 ????????????????identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));60 ????????????????identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));61 ????????????????await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));62 ????????????????if (returnUrl == null)63 ????????????????{64 ????????????????????returnUrl = TempData["returnUrl"]?.ToString();65 ????????????????}66 ????????????????if (returnUrl != null)67 ????????????????{68 ????????????????????return Redirect(returnUrl);69 ????????????????}70 ????????????????else71 ????????????????{72 ????????????????????return RedirectToAction(nameof(HomeController.Index), "Home");73 ????????????????}74 ????????????}75 ????????????else76 ????????????{77 ????????????????const string badUserNameOrPasswordMessage = "用户名或密码错误!";78 ????????????????return BadRequest(badUserNameOrPasswordMessage);79 ????????????}80 ????????}81 ????????[HttpGet("logout")]82 ????????public async Task<IActionResult> Logout()83 ????????{84 ????????????await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);85 ????????????return RedirectToAction("Index", "Home");86 ????????}87 ????????[AllowAnonymous]88 ????????[HttpGet("denied")]89 ????????public IActionResult Denied()90 ????????{91 ????????????return View();92 ????????}93 ????}94 }
前端_Layout.cshtml布局页,在登录成功后的任何页面都可以用@User.Identity.Name就可以获取用户姓名,同时用@User.Claims.SingleOrDefault(s=>s.Type== System.Security.Claims.ClaimTypes.Sid).Value可以获取用户名或角色。
1 ???<nav > 2 ????????<div > 3 ????????????<div > 4 ????????????????<button type="button" data-toggle="collapse" data-target=".navbar-collapse"> 5 ????????????????????<span >Toggle navigation</span> 6 ????????????????????<span ></span> 7 ????????????????????<span ></span> 8 ????????????????????<span ></span> 9 ????????????????</button>10 ????????????????<a asp-area="" asp-controller="Home" asp-action="Index" >RolePrivilegeManagement</a>11 ????????????</div>12 ????????????<div >13 ????????????????<ul >14 ????????????????????<li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>15 ????????????????????<li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>16 ????????????????????<li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>17 ????????????????</ul>18 ????????????????<ul float:right; ">19 ????????????????????<li style="overflow:hidden;">20 ????????????????????????<div style="float:left;line-height:50px;margin-right:10px;">21 ????????????????????????????<span style="color:#ffffff">当前用户:@User.Identity.Name</span>22 ????????????????????????</div>23 ????????????????????????<div style="float:left;line-height:50px;">24 ????????????????????????????<a asp-area="" asp-controller="Home" asp-action="Logout">注销</a>25 ????????????????????????</div>26 ????????????????????</li>27 ????????????????</ul>28 ????????????</div>29 ????????</div>30 ????</nav>
现在可以用chrome运行了,进行登录页后F12,查看Network—Cookies,可以看到有一个Cookie,这个是记录returnUrl的Cookie,是否记得HomeController.cs中的Login Get的Action中代码:TempData["returnUrl"] = returnUrl;这个TempData最后转成了一个Cookie返回到客户端了,如下图:
输入用户名,密码登录,再次查看Cookies,发现多了一个.AspNetCore.Cookies,即把用户验证信息加密码保存在了这个Cookie中,当跳转到别的页面时,这两个Cookie会继续在客户端和服务传送,用以验证用户角色。
二、自定义角色
系统的角色可以自定义,用户是自写到义,权限是固定的,角色对应权限可以自定义,用户对应角色也是自定义的,如下图:
项目代码:
https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/PrivilegeManagement
始于startup.cs
自定义角色与固定角色不同之处在于多了一个中间件(关于中间件学习参看:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware),即在Configure方法中,一定要在app.UseAuthentication下面添加验证权限的中间件,因为UseAuthentication要从Cookie中加载通过验证的用户信息到Context.User中,所以一定放在加载完后才能去验用户信息(当然自己读取Cookie也可以)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Builder; 6 using Microsoft.AspNetCore.Hosting; 7 using Microsoft.Extensions.Configuration; 8 using Microsoft.Extensions.DependencyInjection; 9 using Microsoft.AspNetCore.Authentication.Cookies;10 using Microsoft.AspNetCore.Http;11 using PrivilegeManagement.Middleware;12 13 namespace PrivilegeManagement14 {15 ????public class Startup16 ????{17 ????????public Startup(IConfiguration configuration)18 ????????{19 ????????????Configuration = configuration;20 ????????}21 ????????public IConfiguration Configuration { get; }22 23 ????????public void ConfigureServices(IServiceCollection services)24 ????????{25 ????????????services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)26 ???????????.AddCookie(options =>27 ???????????{28 ???????????????options.LoginPath = new PathString("/login");29 ???????????????options.AccessDeniedPath = new PathString("/denied");30 ???????????}31 ???????????);32 ????????????services.AddMvc();33 ????????}34 35 ????????public void Configure(IApplicationBuilder app, IHostingEnvironment env)36 ????????{37 ????????????if (env.IsDevelopment())38 ????????????{39 ????????????????app.UseDeveloperExceptionPage();40 ????????????????app.UseBrowserLink();41 ????????????}42 ????????????else43 ????????????{44 ????????????????app.UseExceptionHandler("/Home/Error");45 ????????????}46 47 ????????????app.UseStaticFiles();48 ????????????//验证中间件49 ????????????app.UseAuthentication();50 ????????????////添加权限中间件, 一定要放在app.UseAuthentication后51 ????????????app.UsePermission(new PermissionMiddlewareOption()52 ????????????{53 ????????????????LoginAction = @"/login",54 ????????????????NoPermissionAction = @"/denied",55 ????????????????//这个集合从数据库中查出所有用户的全部权限56 ????????????????UserPerssions = new List<UserPermission>()57 ?????????????????{58 ?????????????????????new UserPermission { Url="/", UserName="gsw"},59 ?????????????????????new UserPermission { Url="/home/contact", UserName="gsw"},60 ?????????????????????new UserPermission { Url="/home/about", UserName="aaa"},61 ?????????????????????new UserPermission { Url="/", UserName="aaa"}62 ?????????????????}63 ????????????});64 ????????????app.UseMvc(routes =>65 ????????????{66 ????????????????routes.MapRoute(67 ????????????????????name: "default",68 ????????????????????template: "{controller=Home}/{action=Index}/{id?}");69 ????????????});70 ????????}71 ????}72 }
下面看看中间件PermissionMiddleware.cs,在Invoke中用了context.User,如上面所述,首先要调用app.UseAuthentication加载用户信息后才能在这里使用,这个中间件逻辑较简单,如果没有验证的一律放过去,不作处理,如果验证过(登录成功了),就要查看本次请求的url和这个用户可以访问的权限是否匹配,如不匹配,就跳转到拒绝页面(这个是在Startup.cs中添加中间件时,用NoPermissionAction = @"/denied"设置的)
1 using Microsoft.AspNetCore.Http; 2 using System; 3 using System.Collections.Generic; 4 using System.IO; 5 using System.Linq; 6 using System.Reflection; 7 using System.Security.Claims; 8 using System.Threading.Tasks; 9 10 namespace PrivilegeManagement.Middleware11 {12 ????/// <summary>13 ????/// 权限中间件14 ????/// </summary>15 ????public class PermissionMiddleware16 ????{17 ????????/// <summary>18 ????????/// 管道代理对象19 ????????/// </summary>20 ????????private readonly RequestDelegate _next;21 ????????/// <summary>22 ????????/// 权限中间件的配置选项23 ????????/// </summary>24 ????????private readonly PermissionMiddlewareOption _option;25 26 ????????/// <summary>27 ????????/// 用户权限集合28 ????????/// </summary>29 ????????internal static List<UserPermission> _userPermissions;30 31 ????????/// <summary>32 ????????/// 权限中间件构造33 ????????/// </summary>34 ????????/// <param name="next">管道代理对象</param>35 ????????/// <param name="permissionResitory">权限仓储对象</param>36 ????????/// <param name="option">权限中间件配置选项</param>37 ????????public PermissionMiddleware(RequestDelegate next, PermissionMiddlewareOption option)38 ????????{39 ????????????_option = option;40 ????????????_next = next;41 ????????????_userPermissions = option.UserPerssions;42 ????????} ??????43 ????????/// <summary>44 ????????/// 调用管道45 ????????/// </summary>46 ????????/// <param name="context">请求上下文</param>47 ????????/// <returns></returns>48 ????????public Task Invoke(HttpContext context)49 ????????{50 ????????????//请求Url51 ????????????var questUrl = context.Request.Path.Value.ToLower();52 ???????53 ????????????//是否经过验证54 ????????????var isAuthenticated = context.User.Identity.IsAuthenticated;55 ????????????if (isAuthenticated)56 ????????????{57 ????????????????if (_userPermissions.GroupBy(g=>g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)58 ????????????????{59 ????????????????????//用户名60 ????????????????????var userName = context.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Sid).Value;61 ????????????????????if (_userPermissions.Where(w => w.UserName == userName&&w.Url.ToLower()==questUrl).Count() > 0)62 ????????????????????{63 ????????????????????????return this._next(context);64 ????????????????????}65 ????????????????????else66 ????????????????????{67 ????????????????????????//无权限跳转到拒绝页面68 ????????????????????????context.Response.Redirect(_option.NoPermissionAction);69 ????????????????????}70 ????????????????}71 ????????????}72 ????????????return this._next(context);73 ????????}74 ????}75 }
扩展中间件类PermissionMiddlewareExtensions.cs
1 using Microsoft.AspNetCore.Builder; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Threading.Tasks; 6 ?7 namespace PrivilegeManagement.Middleware 8 { 9 ????/// <summary>10 ????/// 扩展权限中间件11 ????/// </summary>12 ????public static class PermissionMiddlewareExtensions13 ????{14 ????????/// <summary>15 ????????/// 引入权限中间件16 ????????/// </summary>17 ????????/// <param name="builder">扩展类型</param>18 ????????/// <param name="option">权限中间件配置选项</param>19 ????????/// <returns></returns>20 ????????public static IApplicationBuilder UsePermission(21 ??????????????this IApplicationBuilder builder, PermissionMiddlewareOption option)22 ????????{23 ????????????return builder.UseMiddleware<PermissionMiddleware>(option);24 ????????}25 ????}26 }
中间件属性PermissionMiddlewareOption.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 ?6 namespace PrivilegeManagement.Middleware 7 { 8 ????/// <summary> 9 ????/// 权限中间件选项10 ????/// </summary>11 ????public class PermissionMiddlewareOption12 ????{13 ????????/// <summary>14 ????????/// 登录action15 ????????/// </summary>16 ????????public string LoginAction17 ????????{ get; set; }18 ????????/// <summary>19 ????????/// 无权限导航action20 ????????/// </summary>21 ????????public string NoPermissionAction22 ????????{ get; set; }23 24 ????????/// <summary>25 ????????/// 用户权限集合26 ????????/// </summary>27 ????????public List<UserPermission> UserPerssions28 ????????{ get; set; } = new List<UserPermission>();29 ????}30 }
中间件实体类UserPermission.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 ?6 namespace PrivilegeManagement.Middleware 7 { 8 ????/// <summary> 9 ????/// 用户权限10 ????/// </summary>11 ????public class UserPermission12 ????{13 ????????/// <summary>14 ????????/// 用户名15 ????????/// </summary>16 ????????public string UserName17 ????????{ get; set; }18 ????????/// <summary>19 ????????/// 请求Url20 ????????/// </summary>21 ????????public string Url22 ????????{ get; set; }23 ????}24 }
关于自定义角色,因为不需要授权时带上角色,所以可以定义一个基Controller类BaseController.cs,其他的Controller都继承BaseController,这样所有的action都可以通过中间件来验证,当然像登录,无权限提示页面还是在Action上加[AllowAnomymous]
1 using Microsoft.AspNetCore.Authorization;2 using Microsoft.AspNetCore.Mvc;3 namespace PrivilegeManagement.Controllers4 {5 ????[Authorize]6 ????public class BaseController:Controller7 ????{8 ????}9 }
HomeController.cs如下,与固定角色的HomeController.cs差异只在Controller和Action上的Authorize特性。
1 using System; 2 using System.Collections.Generic; 3 using System.Diagnostics; 4 using System.Linq; 5 using System.Threading.Tasks; 6 using Microsoft.AspNetCore.Mvc; 7 using PrivilegeManagement.Models; 8 using Microsoft.AspNetCore.Authorization; 9 using System.Security.Claims;10 using Microsoft.AspNetCore.Authentication.Cookies;11 using Microsoft.AspNetCore.Authentication;12 13 namespace PrivilegeManagement.Controllers14 {15 ?16 ????public class HomeController : BaseController17 ????{18 ????????public IActionResult Index()19 ????????{20 ????????????return View();21 ????????}22 23 ????????public IActionResult About()24 ????????{25 ????????????ViewData["Message"] = "Your application description page.";26 ????????????27 ????????????return View();28 ????????}29 30 ????????public IActionResult Contact()31 ????????{32 ????????????ViewData["Message"] = "Your contact page.";33 34 ????????????return View();35 ????????}36 37 ????????public IActionResult Error()38 ????????{39 ????????????return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });40 ????????}41 ????????[AllowAnonymous]42 ????????[HttpGet("login")]43 ????????public IActionResult Login(string returnUrl = null)44 ????????{45 ????????????TempData["returnUrl"] = returnUrl;46 ????????????return View();47 ????????}48 ????????[AllowAnonymous]49 ????????[HttpPost("login")]50 ????????public async Task<IActionResult> Login(string userName,string password, string returnUrl = null)51 ????????{52 ????????????var list = new List<dynamic> {53 ????????????????new { UserName = "gsw", Password = "111111", Role = "admin",Name="桂素伟" },54 ????????????????new { UserName = "aaa", Password = "222222", Role = "system",Name="测试A" }55 ????????????};56 ????????????var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password);57 ????????????if (user != null)58 ????????????{59 ????????????????//用户标识60 ????????????????var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);61 ????????????????identity.AddClaim(new Claim(ClaimTypes.Sid, userName));62 ????????????????identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));63 ????????????????identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));64 65 ????????????????await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));66 ????????????????if (returnUrl == null)67 ????????????????{68 ????????????????????returnUrl = TempData["returnUrl"]?.ToString();69 ????????????????}70 ????????????????if (returnUrl != null)71 ????????????????{72 ????????????????????return Redirect(returnUrl);73 ????????????????}74 ????????????????else75 ????????????????{76 ????????????????????return RedirectToAction(nameof(HomeController.Index), "Home");77 ????????????????}78 ????????????}79 ????????????else80 ????????????{81 ????????????????const string badUserNameOrPasswordMessage = "用户名或密码错误!";82 ????????????????return BadRequest(badUserNameOrPasswordMessage);83 ????????????}84 ????????}85 ????????[HttpGet("logout")]86 ????????public async Task<IActionResult> Logout()87 ????????{88 ????????????await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);89 ????????????return RedirectToAction("Index", "Home");90 ????????}91 ????????[HttpGet("denied")]92 ????????public IActionResult Denied()93 ????????{94 ????????????return View();95 ????????}96 ????} 97 }
全部代码:https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86
asp.net core 2.0的认证和授权
原文地址:http://www.cnblogs.com/axzxs2001/p/7482771.html