原理:通过自定义策略授权提供者指定授权策略并传入必要的授权参数。
参考文档:
https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-3.1
1 实现Base验证
1.1定义UserInfo类
public class UserInfo
{
public string UserName { get; set; }
public string Password { get; set; }
public IEnumerable<string> Roles { get; set; }
public int Age { get; set; }
/// <summary>
/// 权限
/// </summary>
public IEnumerable<string> Permision { get; set; }
public override string ToString() => UserName;
public static readonly UserInfo[] AllUsers = {
new UserInfo
{
UserName = "daxnet",
Password = "password",
Age = 16,
Roles = new[] { "admin", "super_admin" },
Permision = new[] { "Time_Read", "Time_Add" },//权限
},
new UserInfo
{
UserName = "admin",
Password = "password",
Age = 29,
Roles = new[] { "admin" },
Permision = new[] { "Time_Read" },
}
};
}
1.2 定义BasicAuthenticationSchemeOptions类,配置认证方案选项类
public class BasicAuthenticationSchemeOptions: AuthenticationSchemeOptions
{
}
1.3 定义BasicAuthenticationHandler类,认证方案处理类
public class BasicAuthenticationHandler: AuthenticationHandler<BasicAuthenticationSchemeOptions>
{
public BasicAuthenticationHandler(
IOptionsMonitor<BasicAuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock): base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
{
return Task.FromResult(AuthenticateResult.Fail("没有指定验证头"));
}
string authHeader = Request.Headers["Authorization"].ToString();
if(!authHeader.StartsWith("Basic "))
{
return Task.FromResult(AuthenticateResult.Fail("验证头格式错误"));
}
var base64EncodedValue = authHeader["Basic ".Length..];
var userNamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(base64EncodedValue));
//获取到Authorization验证头携带的用户数据
var userName = userNamePassword.Split(':')[0];
var password = userNamePassword.Split(':')[1];
//使用模拟数据
var user = UserInfo.AllUsers.FirstOrDefault(u => u.UserName == userName && u.Password == password);
if (user == null)
{
return Task.FromResult(AuthenticateResult.Fail("用户名或者密码错误"));
}
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, user.UserName),
new Claim(ClaimTypes.Role, string.Join(',', user.Roles)),
//new Claim(ClaimTypes.UserData, user.Age.ToString()),
new Claim(ClaimTypes.UserData, string.Join(',', user.Permision)),
};
var claimsPrincipal =
new ClaimsPrincipal(new ClaimsIdentity(
claims,
"Basic",
ClaimTypes.NameIdentifier,
ClaimTypes.Role));
var ticket = new AuthenticationTicket(claimsPrincipal, new AuthenticationProperties
{
IsPersistent = false
}, "Basic");
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
1.4 Startup.cs加入认证方案
在Startup.cs文件里,修改ConfigureServices和Configure方法,加入Authentication的支持。
ConfigureServices:
services.AddAuthentication("Base").AddScheme<BasicAuthenticationSchemeOptions, BasicAuthenticationHandler>("Base", option => { });
Configure:
app.UseAuthentication();
2授权
2.1定义PolicyRequirement 类
public class PolicyRequirement:IAuthorizationRequirement
{
public string PermisionName { get; private set; }
public PolicyRequirement(string permisionName)
{
this.PermisionName = permisionName;
}
}
2.2定义PolicyProvider类
ASP.NET Core 应用使用接口的实现 IAuthorizationPolicyProvider 来检索授权策略。 默认情况下, DefaultAuthorizationPolicyProvider 已注册并使用。 DefaultAuthorizationPolicyProvider 返回 AuthorizationOptions 调用中提供的策略 IServiceCollection.AddAuthorization 。
通过 IAuthorizationPolicyProvider 在应用程序的 依赖关系注入 容器中注册不同的实现来自定义此行为。
public class PolicyProvider : IAuthorizationPolicyProvider
{
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
public PolicyProvider(IOptions<AuthorizationOptions> options)
{
FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
}
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
//需要返回GetDefaultPolicyAsync,
//直接抛出未实现异常会出现不能调用 GetPolicyAsync方法
return FallbackPolicyProvider.GetDefaultPolicyAsync();
}
public Task<AuthorizationPolicy> GetFallbackPolicyAsync()
{
return FallbackPolicyProvider.GetDefaultPolicyAsync();
}
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
var policyBuilder = new AuthorizationPolicyBuilder("Base");
//通过添加PolicyRequirement后会使用PolicyAuthorizationHandler作为授权处理类
policyBuilder.AddRequirements(new PolicyRequirement(policyName));
return Task.FromResult(policyBuilder.Build());
//return Task.FromResult<AuthorizationPolicy>(null);
}
}
2.3 定义PolicyAuthorizationHandler类
public class PolicyAuthorizationHandler : AuthorizationHandler<PolicyRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
{
if (context.User != null)
{
var identity = context.User.Identity as System.Security.Claims.ClaimsIdentity;
if (identity == null)
{
context.Fail();
return Task.CompletedTask;
}
var permisions = identity.Claims.FirstOrDefault(c => c.Type == ClaimTypes.UserData);
if (permisions != null && permisions.Value.Split(',').Contains(requirement.PermisionName))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
return Task.CompletedTask;
}
}
2.4 定义PolicyAuthorizeAttribute类
public class PolicyAuthorizeAttribute: AuthorizeAttribute
{
private string permissionName;
public PolicyAuthorizeAttribute(string permissionName)
{
this.PermissionName = permissionName;
}
public string PermissionName
{
get
{
return permissionName;
}
set
{
permissionName = value;
Policy = value;
}
}
}
2.5 Startup.cs注入IauthorizationHandler和IAuthorizationPolicyProvider
ConfigureServices方法:
services.AddSingleton<IAuthorizationHandler, PolicyAuthorizationHandler>();
services.AddSingleton<IAuthorizationPolicyProvider, PolicyProvider>();
Configure方法:
app.UseAuthorization();
2.6 添加TimeController,对控制器的方法进行权限控制
[Authorize]
[ApiController]
[Route("[controller]/[action]")]
public class TimeController : ControllerBase
{
[PolicyAuthorize("Time_Read")]
public string Read()
{
var u = this.User;
return "read "+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
[PolicyAuthorize("Time_Add")]
public string Add()
{
return "add " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
}
效果:

