授权
若没有看过上一篇文章,请先看一下上一篇文章
基于角色的授权(RBAC)
自定义认证处理器(认证逻辑有变化)
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
namespace CustomAuthentication.AuthenticationHandlers;
//自定义选项
public class SecretAuthenticationOptions : AuthenticationSchemeOptions
{
//自定义方案名
public const string Scheme = "Secret";
}
//自定义角色
public static class SecretRoles
{
//小红的朋友
public const string XiaoHongFriend = "XiaoHongFriend";
//小红的爱慕者
public const string XiaoHongAdorer = "XiaoHongAdorer";
}
public class SecretAuthenticationHandler : AuthenticationHandler<SecretAuthenticationOptions>
{
public SecretAuthenticationHandler(IOptionsMonitor<SecretAuthenticationOptions> options, ILoggerFactory logger,
UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
private Task<AuthenticateResult> createSingleIdentityAuthenticationTicket(IEnumerable<Claim> claims)
{
//创建主体
var claimsPrincipal = new ClaimsPrincipal();
//创建证件
var claimsIdentity = new ClaimsIdentity();
//为证件添加信息
claimsIdentity.AddClaims(claims);
//为主体添加证件
claimsPrincipal.AddIdentity(claimsIdentity);
//返回认证票据
return Task.FromResult(
AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, SecretAuthenticationOptions.Scheme)));
}
//小红的朋友
private string[] xiaohongFriends = { "小红", "小芳", "小樱","小华" };
//小红的爱慕者
private string[] xiaohongAdorers = { "小明", "小李", "小华" };
//具体认证过程
//若该方案是默认方案,无论任何时候(action存不存在,有没有标注Authorize特性),该方法都会执行
//若不是默认方案,则仅在标注了Authorize特性,并指定了该方案的情况下,该方法才会执行
//若认证成功,会将返回值中的claimsPrincipal赋值给HttpContext.User,后续在action中可以访问
//ClaimTypes.Role为预定义的Claim类型
//ClaimTypes.Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var nameStringValues = Request.Query["name"];
if (nameStringValues.Count != 1) return Task.FromResult(AuthenticateResult.NoResult());
var name = nameStringValues.ToString();
var claims = new List<Claim>(3);
if (xiaohongFriends.Contains(name)) claims.Add(new Claim(ClaimTypes.Role,SecretRoles.XiaoHongFriend));
if (xiaohongAdorers.Contains(name)) claims.Add(new Claim(ClaimTypes.Role,SecretRoles.XiaoHongAdorer));
if(claims.Count == 0) return Task.FromResult(AuthenticateResult.NoResult());
claims.Add(new Claim("name",name));
return createSingleIdentityAuthenticationTicket(claims);
}
}
Or
[Route("[controller]/[action]")]
public class SecretSpaceController : ControllerBase
{
//小红的朋友或小红的爱慕者都可访问
[Authorize(Roles = $"{SecretRoles.XiaoHongFriend},{SecretRoles.XiaoHongAdorer}")]
[HttpGet]
public string OrSecret1() => $"{HttpContext.User.FindFirst("name")?.Value ?? "无名"}知道了小明喜欢小红";
}
And
[Route("[controller]/[action]")]
public class SecretSpaceController : ControllerBase
{
//同时是小红的朋友和小红的爱慕者才可以访问(只有小华可以)
[Authorize(Roles = SecretRoles.XiaoHongAdorer)]
[Authorize(Roles = SecretRoles.XiaoHongFriend)]
[HttpGet]
public string AndSecret1() => $"{HttpContext.User.FindFirst("name")?.Value ?? "无名"}知道了小明喜欢小红";
}
基于策略的授权
Program.cs添加AddAuthorization
//添加授权策略
builder.Services.AddAuthorization(options =>
{
//若Authorize特性中没有指定策略和角色,则使用该策略
options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireRole(SecretRoles.XiaoHongAdorer,SecretRoles.XiaoHongFriend).Build();
//添加策略
options.AddPolicy("策略名", authorizationPolicyBuilder =>
{
//使用AuthorizationPolicyBuilder添加具体策略
});
});
自定义授权处理器
同时实现AuthorizationHandler和IAuthorizationRequirement
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
namespace CustomAuthentication.AuthorizationHandlers;
public class SecretAuthorizationHandler : AuthorizationHandler<SecretAuthorizationHandler>,IAuthorizationRequirement
{
private readonly string[] roles;
public SecretAuthorizationHandler(params string[] roles)
{
this.roles = roles;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SecretAuthorizationHandler requirement)
{
if (context.User.Claims.Where(claim => claim.Type == ClaimTypes.Role).Any(claim => requirement.roles.Contains(claim.Value)))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
context.Fail();
return Task.CompletedTask;
}
}
options.AddPolicy("RolesPolicy", authorizationPolicyBuilder =>
{
//对于每个Requirement,若有一个处理器调用了context.Fail()或没有任何处理器调用了context.Succeed(Requirement),则该Requirement检验失败,否则检验成功
//若所有的Requirement都检验成功,则授权成功,否则授权失败
//这里添加了2个Requirement和2个Handler,每个Requirement都需要被所有Handler处理,总共调用了4次
authorizationPolicyBuilder.AddRequirements(new SecretAuthorizationHandler(SecretRoles.XiaoHongAdorer));
authorizationPolicyBuilder.AddRequirements(new SecretAuthorizationHandler(SecretRoles.XiaoHongFriend));
});
[Authorize(Policy = "RolesPolicy")]
[HttpGet]
public string Secret1() => $"{HttpContext.User.FindFirst("name")?.Value ?? "无名"}知道了小明喜欢小红";
分别实现AuthorizationHandler和IAuthorizationRequirement
public class SecretAuthorizationRequirement : IAuthorizationRequirement
{
public IEnumerable<string> Roles { get; set; }
}
public class SecretAuthorizationHandler : AuthorizationHandler<SecretAuthorizationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SecretAuthorizationRequirement requirement)
{
if (context.User.Claims.Where(claim => claim.Type == ClaimTypes.Role)
.Any(claim => requirement.Roles.Contains(claim.Value)))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
context.Fail();
return Task.CompletedTask;
}
}
builder.Services.AddSingleton<IAuthorizationHandler,SecretAuthorizationHandler>();
options.AddPolicy("RolesPolicy", authorizationPolicyBuilder =>
{
//对于每个Requirement,若有一个处理器调用了context.Fail()或没有任何处理器调用了context.Succeed(Requirement),则该Requirement检验失败,否则检验成功
//若所有的Requirement都检验成功,则授权成功,否则授权失败
authorizationPolicyBuilder.AddRequirements(new SecretAuthorizationRequirement { Roles = new []{SecretRoles.XiaoHongAdorer} });
authorizationPolicyBuilder.AddRequirements(new SecretAuthorizationRequirement { Roles = new []{SecretRoles.XiaoHongFriend} });
});
直接实现IAuthorizationHandler,处理多个Requirement
public class SecretAuthorizationHandler : IAuthorizationHandler
{
public async Task HandleAsync(AuthorizationHandlerContext context)
{
//context.Requirements => 所有的Requirement
//context.PendingRequirements => 所有未被标记为succeeded的Requirement
//AuthorizationHandler<IAuthorizationRequirement>也是在此方法内根据Requirement的类型判断是否调用HandleRequirementAsync
//后面再根据Requirement的具体类型进行分别处理...
}
}
AuthorizationPolicyBuilder中的其他方法
options.AddPolicy("RolesPolicy", authorizationPolicyBuilder =>
{
//和在Authorize特性中指定方案名效果一样
authorizationPolicyBuilder.AddAuthenticationSchemes(SecretAuthenticationOptions.Scheme);
//以下方法内部都是利用前面的方法实现的
authorizationPolicyBuilder.RequireRole(SecretRoles.XiaoHongAdorer,SecretRoles.XiaoHongFriend);
authorizationPolicyBuilder.RequireClaim(ClaimTypes.Role);
authorizationPolicyBuilder.RequireClaim(ClaimTypes.Role,SecretRoles.XiaoHongAdorer,SecretRoles.XiaoHongFriend);
authorizationPolicyBuilder.RequireAssertion(authorizationHandlerContext =>
{
//利用authorizationHandlerContext做一些判断
return false;
});
});
策略提供器(实现动态策略)
public class CustomAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
//默认策略提供器
private IAuthorizationPolicyProvider defaultPolicyProvider;
public CustomAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
{
defaultPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
}
public async Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
{
var policy = await defaultPolicyProvider.GetPolicyAsync(policyName);
if (policy is not null)
{
//如果策略是硬编码的,由默认策略提供器处理就行
return policy;
}
var builder = new AuthorizationPolicyBuilder();
//根据策略名对builder做些处理...
return builder.Build();
}
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => defaultPolicyProvider.GetDefaultPolicyAsync();
public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => defaultPolicyProvider.GetFallbackPolicyAsync();
}
builder.Services.AddSingleton<IAuthorizationPolicyProvider,CustomAuthorizationPolicyProvider>();
基于资源的授权
using Microsoft.AspNetCore.Authorization;
namespace CustomAuthentication.AuthorizationHandlers;
public class SameAuthorRequirement : IAuthorizationRequirement { }
public class Document
{
public long Id { get; set; }
public string Author { get; set; }
}
public class DocumentAuthorizationHandler :
AuthorizationHandler<SameAuthorRequirement, Document>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
SameAuthorRequirement requirement,
Document resource)
{
if (context.User.Identity?.Name == resource.Author)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("DocumentPolicy", policy =>
policy.Requirements.Add(new SameAuthorRequirement()));
});
builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
[HttpGet]
public async Task<string> GetAuthor([FromQuery] int id,[FromServices] IAuthorizationService authorizationService)
{
Document document = null;
//根据id从数据库查询得到document
//document = ....
//授权结果
var authorizeResult = await authorizationService.AuthorizeAsync(HttpContext.User, document, "DocumentPolicy");
if (authorizeResult.Succeeded)
{
//授权成功...
}
else
{
//授权失败...
}
return "";
}
有扩展重载方法IAuthorizationService.AuthorizeAsync(user , policy),不需要传入资源