zoukankan      html  css  js  c++  java
  • .net core 单体应用基于策略模式授权

    在ASP.NET Core中,重新设计了一种更加灵活的授权方式:基于策略的授权, 它是授权的核心.
    在使用基于策略的授权时,首先要定义授权策略,而授权策略本质上就是对Claims的一系列断言。
    基于角色的授权和基于Scheme的授权,只是一种语法上的便捷,最终都会生成授权策略。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    
        services.AddAuthorization(options =>
        {
            //options.AddPolicy("Administrator", policy => policy.RequireRole("administrator"));
            options.AddPolicy("Administrator", policy => policy.RequireClaim(ClaimTypes.Role, "administrator"));
            
            //options.AddPolicy("Founders", policy => policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
        });
    }
    [Authorize(Policy = "Administrator")]
    public ActionResult<IEnumerable<string>> GetValueByAdminPolicy()
    {
        return new string[] { "GetValueByAdminPolicy" };
    }
    

    自定义策略授权

    基于策略的授权中有一个很重要的概念是Requirements,每一个Requirement都代表一个授权条件。
    Requirement需要继承接口IAuthorizationRequirement。
    在 ASP.NET Core 中已经内置了一些常用的实现:

    AssertionRequirement :使用最原始的断言形式来声明授权策略。
    DenyAnonymousAuthorizationRequirement :用于表示禁止匿名用户访问的授权策略,并在AuthorizationOptions中将其设置为默认策略。
    ClaimsAuthorizationRequirement :用于表示判断Cliams中是否包含预期的Claims的授权策略。
    RolesAuthorizationRequirement :用于表示使用ClaimsPrincipal.IsInRole来判断是否包含预期的Role的授权策略。
    NameAuthorizationRequirement:用于表示使用ClaimsPrincipal.Identities.Name来判断是否包含预期的Name的授权策略。
    OperationAuthorizationRequirement:用于表示基于操作的授权策略。
    除了OperationAuthorizationRequirement外,都有对应的快捷添加方法,比如RequireClaim,RequireRole,RequireUserName等。

    当内置的Requirement不能满足需求时,可以定义自己的Requirement.

    下面演示自定义策略授权

    Startup类配置

     // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddAuthorization(options =>
                {
                    options.AddPolicy("Permission", policy => policy.Requirements.Add(new PermissionRequirement()));
                }).AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
                {
                    options.LoginPath = new PathString("/api/values/login");
                });
                services.AddTransient<IUserService, UserService>();
                services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
                services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
                services.AddControllers();
            }
    

    Configure方法添加

    app.UseAuthentication();
    app.UseAuthorization();
    

    新建类PermissionRequirement

      public class PermissionRequirement : IAuthorizationRequirement
        {
        }
    

    新建类PermissionHandler

    public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
        {
            private readonly IUserService _userService;
            private readonly IHttpContextAccessor _accessor;
    
            public PermissionHandler(IUserService userService, IHttpContextAccessor accessor)
            {
                _userService = userService;
                _accessor = accessor;
            }
    
            protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
            {
                var httpContext = _accessor.HttpContext;
                var isAuthenticated = httpContext.User.Identity.IsAuthenticated;
                if (isAuthenticated)
                {
                    Guid userId;
                    if (!Guid.TryParse(httpContext.User.Claims.SingleOrDefault(s => s.Type == "id").Value, out userId))
                    {
                        return Task.CompletedTask;
                    }
                    var functions = _userService.GetFunctionsByUserId(userId);
                    var requestUrl = httpContext.Request.Path.Value.ToLower();
                    if (functions != null && functions.Count > 0 && functions.Contains(requestUrl))
                    {
                        context.Succeed(requirement);
                    }
                }
                return Task.CompletedTask;
            }
        }
    

    添加测试用户数据,项目中需从数据库读取

    public static class TestUsers
       {
           public static List<User> Users = new List<User>
           {
               new User{ Id = Guid.NewGuid(), UserName = "Paul", Password = "Paul123", Roles = new List<string>{ "administrator", "api_access" }, Urls = new List<string>{ "/api/values/getadminvalue", "/api/values/getguestvalue" }},
               new User{ Id = Guid.NewGuid(), UserName = "Young", Password = "Young123", Roles = new List<string>{ "api_access" }, Urls = new List<string>{ "/api/values/getguestvalue" }},
               new User{ Id = Guid.NewGuid(), UserName = "Roy", Password = "Roy123", Roles = new List<string>{ "administrator" }, Urls = new List<string>{ "/api/values/getadminvalue" }},
           };
       }
    
    
    public class User
       {
           public Guid Id { get; set; }
           public string UserName { get; set; }
           public string Password { get; set; }
           public List<string> Roles { get; set; }
           public List<string> Urls { get; set; }
       }
    

    添加UserService

    public interface IUserService
        {
            List<string> GetFunctionsByUserId(Guid id);
        }
    
     public class UserService : IUserService
        {
            public List<string> GetFunctionsByUserId(Guid id)
            {
                var user = TestUsers.Users.SingleOrDefault(r => r.Id.Equals(id));
                return user?.Urls;
            }
        }
    

    添加api类

    [Authorize(Policy = "Permission")]
        [Route("api/[controller]")]
        [ApiController]
        public class ValuesController : ControllerBase
        {
            [HttpGet("[action]")]
            public ActionResult<IEnumerable<string>> GetAdminValue()
            {
                return new string[] { "use Policy = Permission" };
            }
    
            [HttpGet("[action]")]
            public ActionResult<IEnumerable<string>> GetGuestValue()
            {
                return new string[] { "use Policy = Permission" };
            }
    
          
    
            [AllowAnonymous]
            [HttpPost("[action]")]
            public async Task<ActionResult<bool>> Login([FromBody]UserDto dto)
            {
                var user = TestUsers.Users.FirstOrDefault(s => s.UserName == dto.UserName && s.Password == dto.Password);
                if (user != null)
                {
                    //用户标识
                    var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
                    identity.AddClaim(new Claim(ClaimTypes.Sid, user.UserName));
                    identity.AddClaim(new Claim("id", user.Id.ToString()));
    
    
                    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
    
                    if (dto.ReturnUrl != null)
                    {
                        return Redirect(dto.ReturnUrl);
                    }
                    else
                    {
                        return RedirectToAction(nameof(ValuesController.GetAdminValue), "Values");
                    }
                }
                else
                {
                    const string badUserNameOrPasswordMessage = "用户名或密码错误!";
                    return BadRequest(badUserNameOrPasswordMessage);
                }
            }
    
            public class UserDto
            {
                public string UserName { get; set; }
                public string Password { get; set; }
                public string ReturnUrl { get; set; }
            }
        }
    

    然后我们用Paul用户可以访问所有的接口,Young只能访问getguestvalue接口,Roy只能访问getadminvalue接口。

    文章参考
    ASP.NET Core WebAPI中使用JWT Bearer认证和授权
    asp.net core策略授权

  • 相关阅读:
    错误:严重: Servlet.service() for servlet [appServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is
    转 File "/usr/bin/yum", line 30 except KeyboardInterrupt, e:
    【转】C++和Java比较
    org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'xxxx' is not present
    Leetcode 423. Reconstruct Original Digits from English
    Maven中打包scope为system的Jar包
    Oracle常用函数和注意事项
    Vue中组件之间数据通信
    Vue中data数据响应问题
    JAVA爬虫对font-face字体反爬虫解密
  • 原文地址:https://www.cnblogs.com/KQNLL/p/13651953.html
Copyright © 2011-2022 走看看