认证受权方案之受权初识

1.前言

回顾认证受权方案之JwtBearer认证html

受权

在上一篇中,咱们经过JwtBearer的方式认证,了解在认证时,都是基于Claim的,所以咱们能够经过用户令牌获取到用户的Claims,在受权过程当中对这些Claims进行验证,从而来判断是否具备获取或执行目标资源操做的权限。本章就来介绍一下 ASP.NET Core 的受权系统的简单使用。数据库

2.说明

受权与身份认证是相互独立,可是,受权却须要一种身份验证机制,所以,身份验证能够为当前用户建立一个或多个标识,是肯定用户真实身份的过程。而受权是根据标识肯定用户可执行的操做的过程,其本质就是具备某种特性的用户会有权限访问某个资源或者执行某个操做。例如:一个拥有管理员身份的用户有建立人员、删除人员、编辑人员和删除人员的操做权限,而一个非管理身份的用户仅有读取本身信息的权限。api

这时候,你可能会问,究竟怎样特性的用户能够被受权访问某个资源或执行某个操做。由此咱们引出了受权策略的方式,能够根据用户拥有的角色,也能够根据用户的职位,部门甚至是性别,年龄等等特性进行受权。asp.net

经过创建受权策略方式,检验认证的用户所携带的身份声明(ClaimsPrincipal对象)与受权策略是否一致,从而肯定用户能否执行操做。dom

受权

3.受权

3.1. 基于角色

3.1.1 添加角色

将角色赋予某个控制器或控制器内的操做,指定当前用户必须是其角色才能访问请求资源。ide

可使用Authorize属性的Roles特性指定所请求资源的角色。学习

例如:ui

  • 分配了“admin”角色用户进行访问操做
[Authorize(Roles ="admin")]
public class WeatherForecastController : ControllerBase
{

}
  • 以逗号分隔角色名来允行多个角色访问操做
[Authorize(Roles ="admin,user")]
public class WeatherForecastController : ControllerBase
{ 


}

其中只要知足admmin或者user其一就能够进行访问。.net

  • 同时知足指定的多个角色进行的访问操做
[Authorize(Roles = "admin")]
[Authorize(Roles = "user")]
public class WeatherForecastController : ControllerBase
{ 
}

3.1.2 添加策略的角色

能够建立策略的方式进行访问控制,在配置受权服务中添加注册受权服务策略。设计

在Startup.cs文件中,经过ConfigureServices()配置服务,建立一个容许具备admin角色的用户才能进行访问的策略

public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        //添加受权角色策略
        services.AddAuthorization(options =>
        {
            options.AddPolicy("BaseRole", options => options.RequireRole("admin"));
        });
        //或者指定多个容许的角色
        //services.AddAuthorization(options =>
        // {
        //    options.AddPolicy("MoreBaseRole", options => options.RequireRole("admin","user"));
        // });
    }

在控制器方法使用特性Policy的属性进行策略应用

[Authorize(Policy = "BaseRole")]
    public class WeatherForecastController : ControllerBase
    {
    
    }

3.2. 基于声明

3.2.1添加声明

对当前用户必须拥有的声明,并将声明赋予某个控制器或控制器内的操做,所以,指定声明必须持有对应的值才能访问请求资源。

声明要求基于策略,因此必须进行构建一个表示声明要求的策略,才能进行受权。

最简单的类型声明是将判断声明是否存在,而不检查值。

能够建立策略的方式进行访问控制,在配置受权服务中添加注册受权服务策略。

在Startup.cs文件中,经过ConfigureServices()配置服务,建立一个容许具备声明的用户才能进行访问的策略

public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        //添加基于声明的受权
        services.AddAuthorization(options =>
        {
            options.AddPolicy("BaseClaims", options => options.RequireClaim("name"));
        });
    }

BaseClaims声明策略会检查name当前标识是否存在声明。

在控制器方法使用特性Policy的属性进行策略应用

[Authorize(Policy = "BaseClaims")]
    public class WeatherForecastController : ControllerBase
    {
    
    }

可是,大多时候,咱们须要声明包含值,只有指定容许值的列表,才能受权成功。因此,能够添加指定值。

public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        //添加基于声明的受权,指定容许值列表。
        services.AddAuthorization(options =>
        {
            options.AddPolicy("BaseClaims", options => options.RequireClaim("name","i3yuan"));
        });
    }

3.3 基于策略

上面介绍的基于角色和基于声明的受权,都使用了要求、要求处理程序和预配置的策略。这些在构建上提供了便捷,可是最终都是生成受权策略。ASP.NET Core,设计了另外一种灵活的受权方式,一种更丰富的可重复使用的受权结构,基于策略的受权,同时这也是受权的核心。

这节会先讲一下受权策略的应用,在下一节中,会对受权策略的核心进行一步步的详解。

在上面咱们简单的介绍了基于策略的角色受权,可是这种方式无非基于角色或者声明多一些。

所以,这里咱们基于自定义策略受权的方式,实现受权。

自定义受权,就要咱们本身写策略提供器,本身根据不一样的参数来生成不一样的策略,从新实现策略的方式。策略要求由如下两种元素组成:仅保留数据的要求类,以及对用户验证数据的受权处理程序。建立自定义要求,还能够进一步表达特定策略。

3.3.1. 定义权限策略PermissionRequirement

定义一个权限策略,这个策略并包含一些属性。

public class PermissionRequirement: IAuthorizationRequirement
{
    public string _permissionName { get; }

    public PermissionRequirement(string PermissionName)
    {
        _permissionName = PermissionName;
    }
}

3.3.2. 再定义一个策略处理类

public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
    {
        var role = context.User.FindFirst(c => c.Type == ClaimTypes.Role);
        if (role != null)
        {
            var roleValue = role.Value;
            if (roleValue==requirement._permissionName)
            {
                context.Succeed(requirement);
            }
        }
        return Task.CompletedTask;

受权处理程序读取与角色用户关联的声明,并检查自定义的角色,若是角色匹则成功,不然没法返回成功。

这里的自定义声明是写固定了,可是也能够经过数据库或外部服务的方式进行运行查询获取用户相关角色信息相对应的判断条件,从而在处理程序中进行判断处理。

受权处理程序调用方法 Succeed,同时传递当前要求,以通知此要求已成功获得验证。若是没有传递要求,处理程序无需执行任何操做,能够直接返回内容。不过,若是处理程序要肯定是否不符合要求(不管其余处理程序是否已成功验证同一要求),将会对受权上下文对象调用方法 Fail

3.3.3. 下面展现了如何将自定义要求添加到策略

(请注意,因为这是自定义要求,所以没有扩展方法,而必须继续处理策略对象的整个 Requirements 集合):

public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        //基于自定义策略受权
        services.AddAuthorization(options =>
        {
            options.AddPolicy("customizePermisson",
              policy => policy
                .Requirements
                .Add(new PermissionRequirement("admin")));
        });
        //此外,还须要在 IAuthorizationHandler 类型的范围内向 DI 系统注册新的处理程序:
        services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();
        // 如前所述,要求可包含多个处理程序。若是为受权层的同一要求向 DI 系统注册多个处理程序,有一个成功就足够了。

    }

3.3.4. 应用自定义的策略的特性

指定当前用户必须是应用对控制器或控制器内的操做,如

[Authorize(Policy = "customizePermisson")]
    public class WeatherForecastController : ControllerBase
    { 
    }

4.场景

在上一篇认证受权方案之JwtBearer认证中,咱们已经实现了获取token的方式,这一次,咱们实现一个以基于角色场景为例的认证受权。

在原来生成token的方式中,添加多一个声明角色的Claim,以下:

new Claim(JwtClaimTypes.Role,"admin")

[HttpGet]
    public IActionResult GetToken()
    {
        try
        {
            //定义发行人issuer
            string iss = "JWTBearer.Auth";
            //定义受众人audience
            string aud = "api.auth";
            //定义许多种的声明Claim,信息存储部分,Claims的实体通常包含用户和一些元数据
            IEnumerable<Claim> claims = new Claim[]
            {
                new Claim(JwtClaimTypes.Id,"1"),
                new Claim(JwtClaimTypes.Name,"i3yuan"),
                new Claim(JwtClaimTypes.Role,"admin"),
            };
            //notBefore  生效时间
            // long nbf =new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
            var nbf = DateTime.UtcNow;
            //expires   //过时时间
            // long Exp = new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds();
            var Exp = DateTime.UtcNow.AddSeconds(1000);
            //signingCredentials  签名凭证
            string sign = "q2xiARx$4x3TKqBJ"; //SecurityKey 的长度必须 大于等于 16个字符
            var secret = Encoding.UTF8.GetBytes(sign);
            var key = new SymmetricSecurityKey(secret);
            var signcreds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            //String issuer = default(String), String audience = default(String), IEnumerable<Claim> claims = null, Nullable<DateTime> notBefore = default(Nullable<DateTime>), Nullable<DateTime> expires = default(Nullable<DateTime>), SigningCredentials signingCredentials = null
            var jwt = new JwtSecurityToken(issuer: iss, audience: aud, claims:claims,notBefore:nbf,expires:Exp, signingCredentials: signcreds);
            var JwtHander = new JwtSecurityTokenHandler();
            var token = JwtHander.WriteToken(jwt);
            return Ok(new
            {
                access_token = token,
                token_type = "Bearer",
            });
        }
        catch (Exception ex)
        {
            throw;
        }
    }

对控制器或控制器内的操做,指定当前用户必须是其角色才能访问请求资源,如WeatherForecastController.cs

[ApiController]
[Route("[controller]")]
[Authorize(Roles ="admin")]
public class WeatherForecastController : ControllerBase
{ 
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

5.运行

5.1. 获取token

分别获取role为admin和role为user的状况下颁发的token,只有在角色为admin的状况下才能受权经过。

5.2. 受权资源接口访问

在role为admin的状况下

受权

受权

在role为user的状况下

受权

受权

由上可知,只有在角色为admin的状况下,才能访问目标资源进行操做。

6.总结

  1. 从上一篇的认证到这一篇的受权阶段,简单的介绍了Asp.net Core的认证受权系统,对受权有了初步的认识以及使用,对受权进行划分为两种,一种是基于角色的受权,但随着角色的增长会对处理受权产生限制,不适合表达复杂的受权逻辑。另外一种是基于策略的身份验证,策略包含一系列基于声明的要求,以及基于可从 HTTP 上下文或外部源注入的其余任何信息的自定义逻辑。这些要求各自与一个或多个处理程序相关联,这些处理程序负责要求的实际计算。
  2. 能够发现,asp.net core提供的受权策略是一个很是强大丰富且灵活的认证受权方案,可以知足大部分的受权场景。
  3. 若是有不对的或不理解的地方,但愿你们能够多多指正,提出问题,一块儿讨论,不断学习,共同进步。
  4. 所以,在后续的篇章中,会继续探索受权系统,对受权策略的核心进行一步步的详解。
  5. 本示例源码地址

参考文献文档

相关文章
相关标签/搜索