asp.net core的认证和授权
在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的一些信息,用以后
角色和权限的关系时,所有Action只有admin和system两个角色能访问到,About上的[Authorize(Roles=”admin”)]声明这个action只能admin角色访问,Contact上的[Authorize(Roles=”system”)]声明这个action只能system角色访问,如果action上声明的是[AllowAnomymous],说明不受授权管理,可以直接访问。using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using RolePrivilegeManagement.Models; using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; namespace RolePrivilegeManagement.Controllers { [Authorize(Roles = "admin,system")] public class HomeController : Controller { public IActionResult Index() { return View(); } [Authorize(Roles = "admin")] public IActionResult About() { ViewData["Message"] = "Your application description page."; return View(); } [Authorize(Roles = "system")] public IActionResult Contact() { ViewData["Message"] = "Your contact page."; return View(); } public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } [AllowAnonymous] [HttpGet("login")] public IActionResult Login(string returnUrl = null) { TempData["returnUrl"] = returnUrl; return View(); } [AllowAnonymous] [HttpPost("login")] public async Task<IActionResult> Login(string userName, string password, string returnUrl = null) { var list = new List<dynamic> { new { UserName = "gsw", Password = "111111", Role = "admin",Name="桂素伟" }, new { UserName = "aaa", Password = "222222", Role = "system",Name="测试A" } }; var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password); if (user!=null) { //用户标识 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Sid, userName)); identity.AddClaim(new Claim(ClaimTypes.Name, user.Name)); identity.AddClaim(new Claim(ClaimTypes.Role, user.Role)); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity)); if (returnUrl == null) { returnUrl = TempData["returnUrl"]?.ToString(); } if (returnUrl != null) { return Redirect(returnUrl); } else { return RedirectToAction(nameof(HomeController.Index), "Home"); } } else { const string badUserNameOrPasswordMessage = "用户名或密码错误!"; return BadRequest(badUserNameOrPasswordMessage); } } [HttpGet("logout")] public async Task<IActionResult> Logout() { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return RedirectToAction("Index", "Home"); } [AllowAnonymous] [HttpGet("denied")] public IActionResult Denied() { return View(); } } }前端_Layout.cshtml布局页,在登录成功后的任何页面都可以用@User.Identity.Name就可以获取用户姓名,同时用@User.Claims.SingleOrDefault(s=>s.Type==System.Security.Claims.ClaimTypes.Sid).Value可以获取用户名或角色。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>@ViewData["Title"] - RolePrivilegeManagement</title> <environment include="Development"> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" /> <link rel="stylesheet" href="~/css/site.css" /> </environment> <environment exclude="Development"> <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> </environment> <style> /* 未访问的链接 */ a.logout:link { color: #9d9d9d } /* 已访问的链接 */ a.logout:visited { color: #9d9d9d } /* 当有鼠标悬停在链接上 */ a.logout:hover { color: #ffffff } /* 被选择的链接 */ a.l 1e92b ogout:active { color: #9d9d9d } a.logout{ text-decoration:none; } </style> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">RolePrivilegeManagement</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li> <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li> <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li> </ul> <ul class="" style="float:right; margin:0;"> <li style="overflow:hidden;"> <div style="float:left;line-height:50px;margin-right:10px;"> <span style="color:#ffffff">当前用户:@User.Identity.Name</span> </div> <div style="float:left;line-height:50px;"> <a asp-area="" asp-controller="Home" asp-action="Logout" class="logout">注销</a> </div> </li> </ul> </div> </div> </nav> <div class="container body-content"> @RenderBody() <hr /> <footer> <p>© 2017 - RolePrivilegeManagement</p> </footer> </div> <environment include="Development"> <script src="~/lib/jquery/dist/jquery.js"></script> <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> <script src="~/js/site.js" asp-append-version="true"></script> </environment> <environment exclude="Development"> <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js" asp-fallback-src="~/lib/jquery/dist/jquery.min.js" asp-fallback-test="window.jQuery" crossorigin="anonymous" integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk"> </script> <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js" asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" crossorigin="anonymous" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"> </script> <script src="~/js/site.min.js" asp-append-version="true"></script> </environment> @RenderSection("Scripts", required: false) </body> </html>现在可以用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也可以)
扩展中间件类PermissionMiddlewareExtensions.cs[code=c#;toolbar:false">using Microsoft.AspNetCore.Builder; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PrivilegeManagement.Middleware { /// <summary> /// 扩展权限中间件 /// </summary> public static class PermissionMiddlewareExtensions { /// <summary> /// 引入权限中间件 /// </summary> /// <param name="builder">扩展类型</param> /// <param name="option">权限中间件配置选项</param> /// <returns></returns> public static IApplicationBuilder UsePermission( this IApplicationBuilder builder, PermissionMiddlewareOption option) { return builder.UseMiddleware<PermissionMiddleware>(option); } } }
中间件属性PermissionMiddlewareOption.csusing System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PrivilegeManagement.Middleware { /// <summary> /// 权限中间件选项 /// </summary> public class PermissionMiddlewareOption { /// <summary> /// 登录action /// </summary> public string LoginAction { get; set; } /// <summary> /// 无权限导航action /// </summary> public string NoPermissionAction { get; set; } /// <summary> /// 用户权限集合 /// </summary> public List<UserPermission> UserPerssions { get; set; } = new List<UserPermission>(); } }中间件实体类UserPermission.cs
[code=c#;toolbar:false">using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PrivilegeManagement.Middleware { /// <summary> /// 用户权限 /// </summary> public class UserPermission { /// <summary> /// 用户名 /// </summary> public string UserName { get; set; } /// <summary> /// 请求Url /// </summary> public string Url { get; set; } } }- ASP.NET Core WebAPI中使用JWT Bearer认证和授权
- asp.net core 2.0的认证和授权
- ASP.NET Core 认证与授权[4]:JwtBearer认证
- ASP.NET Core 认证与授权[3]:OAuth & OpenID Connect认证
- ASP.NET Core 认证与授权[4]:JwtBearer认证
- ASP.NET Core 认证与授权[4]:JwtBearer认证
- ASP.NET Core 认证与授权[1]:初识认证
- 5.1基于JWT的认证和授权「深入浅出ASP.NET Core系列」
- asp.net core webapi实现jwt授权认证
- asp.net core-14.JWT认证授权 生成 JWT Token
- ASP.NET Core 认证与授权[6]:授权策略是怎么执行的?
- ASP.NET Core 认证与授权[7]:动态授权
- asp.net core的认证和授权
- ASP.NET Core 认证与授权[2]:Cookie认证
- ASP.NET Core WebAPI中使用JWT Bearer认证和授权
- 【转载】asp.net core 2.0的认证和授权
- ASP.NET Core 认证与授权[5]:初识授权
- ASP.NET Core 认证与授权[6]:授权策略是怎么执行的?
- ASP.NET Core 认证与授权[5]:初识授权
- ASP.NET Core 认证与授权[7]:动态授权