博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
asp.net core策略授权
阅读量:5827 次
发布时间:2019-06-18

本文共 13902 字,大约阅读时间需要 46 分钟。

在《asp.net core认证与授权》中讲解了固定和自定义角色授权系统权限,其实我们还可以通过其他方式来授权,比如可以通过角色组,用户名,生日等,但这些主要取决于ClaimTypes,其实我们也可以自定义键值来授权,这些统一叫策略授权,其中更强大的是,我们可以自定义授权Handler来达到灵活授权,下面一一展开。

注意:下面的代码只是部分代码,完整代码参照:

首先看基于角色组,或用户名,或基于ClaimType或自定义键值等授权策略,这些都是通过Services.AddAuthorization添加,并且是AuthorizationOptions来AddPolicy,这里策略的名称统一用RequireClaim来命名,不同的请求的策略名称各不相同,如用户名时就用policy.RequireUserName(),同时,在登录时,验证成功后,要添加相应的Claim到ClaimsIdentity中:

Startup.cs

       public void ConfigureServices(IServiceCollection services)        {            services.AddMvc();            services.AddAuthorization(options =>            {                //基于角色的策略                 options.AddPolicy("RequireClaim", policy => policy.RequireRole("admin", "system"));                //基于用户名                //options.AddPolicy("RequireClaim", policy => policy.RequireUserName("桂素伟"));                //基于Claim                //options.AddPolicy("RequireClaim", policy => policy.RequireClaim(ClaimTypes.Country,"中国"));                //自定义值                // options.AddPolicy("RequireClaim", policy => policy.RequireClaim("date","2017-09-02"));            }).AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>{                options.LoginPath = new PathString("/login");                options.AccessDeniedPath = new PathString("/denied");            });                  }

HomeController.cs

using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Threading.Tasks;using Microsoft.AspNetCore.Mvc;using PolicyPrivilegeManagement.Models;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Authentication;using Microsoft.AspNetCore.Authentication.Cookies;using System.Security.Claims;namespace PolicyPrivilegeManagement.Controllers{    [Authorize(Policy = "RequireClaim")]    public class HomeController : Controller    {        PermissionHandler _permissionHandler;        public HomeController(IAuthorizationHandler permissionHandler)        {            _permissionHandler = permissionHandler as PermissionHandler;        }        public IActionResult Index()        {            return View();        }        public IActionResult PermissionAdd()        {                       return View();        }        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
 Login(string userName, string password, string returnUrl = null)        {            var list = new List
 {                new { UserName = "gsw", Password = "111111", Role = "admin",Name="桂素伟",Country="中国",Date="2017-09-02",BirthDay="1979-06-22"},                new { UserName = "aaa", Password = "222222", Role = "system",Name="测试A" ,Country="美国",Date="2017-09-03",BirthDay="1999-06-22"}            };            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));                identity.AddClaim(new Claim(ClaimTypes.Country, user.Country));                identity.AddClaim(new Claim("date", user.Date));                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
 Logout()        {            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);            return RedirectToAction("Index", "Home");        }        [AllowAnonymous]        [HttpGet("denied")]        public IActionResult Denied()        {            return View();        }    }}

上面的授权策略都相对简单,单一,使用场景也很有限,就和固定角色授权如出一辙,其实可以用更好的来例用授权,那就是自定义授权Handler,我们在《asp.net core认证与授权》一文中,是通过中间件来达到自定义解色的,现在我们换个思路,通过自定义授权Handler来实现。

首先定义一个UserPermission,即用户权限实体类

    ///     /// 用户权限    ///     public class UserPermission    {        ///         /// 用户名        ///         public string UserName        { get; set; }        ///         /// 请求Url        ///         public string Url        { get; set; }    }

接下来定义一个PermissionRequirement,为请求条件实体类

    ///     /// 必要参数类    ///     public class PermissionRequirement : IAuthorizationRequirement    {        ///         /// 用户权限集合        ///         public  List
 UserPermissions { get;private set; }        /// 
        /// 无权限action        ///         public string DeniedAction { get; set; }        /// 
        /// 构造        ///         /// 
无权限action        /// 
用户权限集合        public PermissionRequirement(string deniedAction, List
 userPermissions)        {            DeniedAction = deniedAction;            UserPermissions = userPermissions;        }    }

再定义自定义授权Hanlder,我们命名为PermissionHandler,此类必需继承AuthorizationHandler<T>,只用实现public virtualTask HandleAsync(AuthorizationHandlerContext context),些方法是用户请求时验证是否授权的主方法,所以实现与自定义角色中间件的Invoke很相似。

using Microsoft.AspNetCore.Authorization;using System.Collections.Generic;using System.Linq;using System.Security.Claims;using System.Threading.Tasks;namespace PolicyPrivilegeManagement.Models{    ///     /// 权限授权Handler    ///     public class PermissionHandler : AuthorizationHandler
    {        /// 
        /// 用户权限        ///         public List
 UserPermissions { get; set; }        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)        {            //赋值用户权限            UserPermissions = requirement.UserPermissions;            //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息            var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;            //请求Url            var questUrl = httpContext.Request.Path.Value.ToLower();            //是否经过验证            var isAuthenticated = httpContext.User.Identity.IsAuthenticated;            if (isAuthenticated)            {                if (UserPermissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)                {                    //用户名                    var userName = httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Sid).Value;                    if (UserPermissions.Where(w => w.UserName == userName && w.Url.ToLower() == questUrl).Count() > 0)                    {                        context.Succeed(requirement);                    }                    else                    {                        //无权限跳转到拒绝页面                        httpContext.Response.Redirect(requirement.DeniedAction);                    }                }                else                {                    context.Succeed(requirement);                }            }            return Task.CompletedTask;        }    }}

此次的Startup.cs的ConfigureServices发生了变化,如下

       public void ConfigureServices(IServiceCollection services)        {            services.AddMvc();            services.AddAuthorization(options =>            {                               //自定义Requirement,userPermission可从数据库中获得                var userPermission = new List
 {                              new UserPermission {  Url="/", UserName="gsw"},                              new UserPermission {  Url="/home/permissionadd", UserName="gsw"},                              new UserPermission {  Url="/", UserName="aaa"},                              new UserPermission {  Url="/home/contact", UserName="aaa"}                          };                options.AddPolicy("Permission",                          policy => policy.Requirements.Add(new PermissionRequirement("/denied", userPermission)));            }).AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>{                options.LoginPath = new PathString("/login");                options.AccessDeniedPath = new PathString("/denied");            });            //注入授权Handler            services.AddSingleton
();        }

HomeController中代码如下:

using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Threading.Tasks;using Microsoft.AspNetCore.Mvc;using PolicyPrivilegeManagement.Models;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Authentication;using Microsoft.AspNetCore.Authentication.Cookies;using System.Security.Claims;namespace PolicyPrivilegeManagement.Controllers{    [Authorize(Policy = "Permission")]    public class HomeController : Controller    {        PermissionHandler _permissionHandler;        public HomeController(IAuthorizationHandler permissionHandler)        {            _permissionHandler = permissionHandler as PermissionHandler;        }        public IActionResult Index()        {            return View();        }        public IActionResult PermissionAdd()        {                       return View();        }        [HttpPost("addpermission")]        public IActionResult AddPermission(string url,string userName)        {                   //添加权限            _permissionHandler.UserPermissions.Add(new UserPermission { Url = url, UserName = userName });            return Content("添加成功");        }                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
 Login(string userName, string password, string returnUrl = null)        {            var list = new List
 {                new { UserName = "gsw", Password = "111111", Role = "admin",Name="桂素伟",Country="中国",Date="2017-09-02",BirthDay="1979-06-22"},                new { UserName = "aaa", Password = "222222", Role = "system",Name="测试A" ,Country="美国",Date="2017-09-03",BirthDay="1999-06-22"}            };            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));                identity.AddClaim(new Claim(ClaimTypes.Country, user.Country));                identity.AddClaim(new Claim("date", user.Date));                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
 Logout()        {            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);            return RedirectToAction("Index", "Home");        }        [AllowAnonymous]        [HttpGet("denied")]        public IActionResult Denied()        {            return View();        }    }}

本例设计是当用户gsw密码111111登录时,是不能访问/home/contact的,刚登录时访该action是不成功的,这里我们在/home/addpermission中添加一个Action名称:/home/contact,用户名:gsw的信息,此时再访问/home/contact,会发现是可以访问的,这是因为我们热更新了PermissionHandler中的用户权限集合,用户的权限得到了扩展和变化。

其实用中间件能达到灵活权限的设置,用自定义授权Handler也可以,接下来比较一下两种做法的优劣:

中间件

自定义授权Handler

用户权限集合

静态对象

实体化对象

热更新时

用中间件名称.用户权限集合更新

因为在Startup.cs中,PermissionHandler是依赖注放的,可以在热更新的构造中获取并操作

性能方面

每个action请求都会触发Invock方法,标记[AllowAnonymous]特性的Action也会触发

只有标记[Authorize]特性的Action会触发该方法,标记[AllowAnonymous]特性的Action不会触发,性能更优化

最后,把授权策略做了个NuGet的包,大家可在asp.net core 2.0的项目中查询 AuthorizePolicy引用使用这个包,包对应的github地址:https://github.com/axzxs2001/AuthorizePolicy,欢迎大家提出建议,来共同完善这个授权策略。

转载地址:http://wfadx.baihongyu.com/

你可能感兴趣的文章
线程间操作无效: 从不是创建控件的线程访问它的解决方法
查看>>
hdu 1236 排名
查看>>
PHP面向对象深入研究之【继承】,减少代码重复
查看>>
RBAC权限管理
查看>>
此博客不再发表对自己私事的看法
查看>>
导致Asp.Net站点重启的10个原因
查看>>
【PMP】Head First PMP 学习笔记 第一章 引言
查看>>
抓住云机遇编排工作 搞定复杂IT工作流
查看>>
MYSQL的longtext字段能放多少数据?
查看>>
MTK 平台上如何给 camera 添加一种 preview size
查看>>
云计算最大难处
查看>>
关于数据分析思路的4点心得
查看>>
Memcached安装与配置
查看>>
美团数据仓库的演进
查看>>
SAP被评为“大数据”预测分析领军企业
查看>>
联想企业网盘张跃华:让文件创造业务价值
查看>>
记录一次蚂蚁金服前端电话面试
查看>>
直播源码开发视频直播平台,不得不了解的流程
查看>>
Ubuntu上的pycrypto给出了编译器错误
查看>>
聊聊flink的RestClientConfiguration
查看>>