前两篇文章扫盲篇进阶篇中介绍了基本的asp.net core 中基于策略的授权的使用方法。

使用策略授权时,只能指定策略,不能配置其他信息。

1
2
3
4
5
[Authorize(Policy = "AtLeast21")]//指定要验证的策略
public class AlcoholPurchaseController : Controller
{
    public IActionResult Index() => View();
}

  

如果我们有很多种授权逻辑,就要有很多种策略。有没有一种可能,可以在指定授权策略时,传入一些参数,灵活的控制授权呢。

当然是可以的。

我们包装一个自己的验证特性PermissionAuthorizeAttribute类,继承自AuthorizeAttribute。构造函数允许传入权限类型参数,并且执行父类构造函数时指定策略“Permission”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// <summary>
/// 继承自AuthorizeAttribute,AuthorizeAttribute要求指定策略,那这里就固定为Permission。
/// 支持接受一个权限集合(Permission[]),可以指定要验证的权限
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAuthorizeAttribute : AuthorizeAttribute
{
    /// <summary>
    /// </summary>
    /// <param name="permissions"></param>
    public PermissionAuthorizeAttribute(params Permission[] permissions) : base("Permission") => Permissions = permissions;
 
    public Permission[] Permissions { get; set; }
}

  权限类型是一个枚举,标记什么样的权限,这里就简单的做个示例。允许传入多个,应该是or的关系。如果要验证多个权限类型,是并且的关系。那应该加多个特性。

1
2
3
4
5
6
7
8
9
10
11
12
public enum Permission
{
    /// <summary>
    /// 用户有明细
    /// </summary>
    HasName = 1,
 
    /// <summary>
    /// 用户有邮箱
    /// </summary>
    HasEmail = 2,
}

  使用的时候如下,可以看到访问HasNameController时,授权校验了Permission.HasName权限。

1
2
3
4
5
6
7
8
9
10
11
12
[PermissionAuthorize(Permission.HasName)]
public class HasNameController : Controller
{
    public IActionResult Index() => View();
}
 
 
[PermissionAuthorize(Permission.HasEmail)]
public class HasEmailController : Controller
{
    public IActionResult Index() => View();
}

  

那么handler中怎么拿到权限类型信息呢?我们知道handler校验函数有两个参数,一个是TRequirement,startup类中配置策略时创建的,这个是固定的。一个是AuthorizationHandlerContext,上下文。听听,上下文,就感觉应该从这里拿。

AuthorizationHandlerContext有个Resource属性,解释是“The optional resource to evaluate the Requirements against.”要对Requirements进行评估的可选资源。我理解如果你验证特性放在controller上,Resource就是这个controller。如果你验证特性放在action上,Resource就是这个action。我们使用在controller上了,那么我们就可以通过controller对象找到其上面的特性信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
        PermissionRequirement requirement)
    {
        var attributes = new List<PermissionAuthorizeAttribute>();
 
        if ((context.Resource as RouteEndpoint) is RouteEndpoint route)
        {
            //通过元数据Metadata能找到其上的PermissionAuthorizeAttribute特性实例
            var tempAttributes = route.Metadata.Where(x => x is PermissionAuthorizeAttribute);
            if (tempAttributes != null && tempAttributes.Count() > 0)
            {
                attributes.AddRange(tempAttributes.Select(x => (PermissionAuthorizeAttribute)x));
            }
        }
        //特性实例上的Permissions信息
        var permissions = attributes.Select(x => x.Permissions);
 
        //后面就是自己的验证逻辑了
        foreach (var item in permissions)
        {
            if (item.Any(x => x == Permission.HasName) && !context.User.HasClaim(c => c.Type == ClaimTypes.Name))
            {
                context.Fail();
                return Task.CompletedTask;
            }
            if (item.Any(x => x == Permission.HasEmail) && !context.User.HasClaim(c => c.Type == ClaimTypes.Email))
            {
                context.Fail();
                return Task.CompletedTask;
            }
        }
        context.Succeed(requirement);
        return Task.CompletedTask;
    }
}

  startup正常注册就行

1
2
3
4
5
6
7
8
9
services.AddAuthorization(options =>
            {
                options.AddPolicy("Permission", policy =>
                    policy.Requirements.Add(new PermissionRequirement()));
  
  
            });
  
            services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();

  来自:https://blog.csdn.net/yangguosheng/article/details/129169277