asp.net core策略授权

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

注意:下面的代码只是部分代码,完整代码参照:https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/PolicyPrivilegeManagement

首先看基于角色组,或用户名,或基于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("桂素伟"));

                //基于ClaimType

                //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;

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

    {       

        public IActionResult Index()

        {

            return View();

        }


        public IActionResult About()

        {

            ViewData["Message"] = "Your application description page.";

            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<IActionResult> Login(string userName, string password, string returnUrl = null)

        {

            var list = new List<dynamic> {

                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<IActionResult> Logout()

        {

            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

            return RedirectToAction("Index", "Home");

        }

        [AllowAnonymous]

        [HttpGet("denied")]

        public IActionResult Denied()

        {

            return View();

        }

    }

}

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

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

/// <summary>

    /// 用户权限

    /// </summary>

    public class UserPermission

    {

        /// <summary>

        /// 用户名

        /// </summary>

        public string UserName

        { get; set; }

        /// <summary>

        /// 请求Url

        /// </summary>

        public string Url

        { get; set; }

    }


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


/// <summary>

    /// 必要参数类

    /// </summary>

    public class PermissionRequirement : IAuthorizationRequirement

    {

        /// <summary>

        /// 用户权限集合

        /// </summary>

        public  List<UserPermission> UserPermissions { get;private set; }

        /// <summary>

        /// 无权限action

        /// </summary>

        public string DeniedAction { get; set; }

        /// <summary>

        /// 构造

        /// </summary>

        /// <param name="deniedAction">无权限action</param>

        /// <param name="userPermissions">用户权限集合</param>

        public PermissionRequirement(string deniedAction, List<UserPermission> userPermissions)

        {

            DeniedAction = deniedAction;

            UserPermissions = userPermissions;

        }

    }


再定义自定义授权Hanlder,我们命名为PermissionHandler,此类必需继承AuthorizationHandler<T>,只用实现public virtual Task 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

{

    /// <summary>

    /// 权限授权Handler

    /// </summary>

    public class PermissionHandler : AuthorizationHandler<PermissionRequirement>

    {

        /// <summary>

        /// 用户权限

        /// </summary>

        public List<UserPermission> 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("/denied");

                    }

                }

                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<UserPermission> {

                              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<IAuthorizationHandler, PermissionHandler>();

        }

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<IActionResult> Login(string userName, string password, string returnUrl = null)

        {

            var list = new List<dynamic> {

                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<IActionResult> 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不会触发,性能更优化


相关文章: 

原文地址:http://www.cnblogs.com/axzxs2001/p/7482777.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值