ASP.NET Core JWT授权验证----自定义处理类

- JWT IAuthorizationRequirement

一、 JWT配置 配置文件添加节点

 "Audience": {
   "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+
   "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。内容就是Secret
   "Issuer": "Blog.Core",
   "Audience": "wr"
 }

二、新增 类 PermissionRequirement 继承 IAuthorizationRequirement,用于设计自定义权限处理器PermissionHandler

public class PermissionRequirement : IAuthorizationRequirement
{
    /// <summary>
    /// 用户权限集合,一个订单包含了很多详情,
    /// 同理,一个网站的认证发行中,也有很多权限详情(这里是Role和URL的关系)
    /// </summary>
    public List<PermissionItem> Permissions { get; set; }
    /// <summary>
    /// 无权限action
    /// </summary>
    public string DeniedAction { get; set; }

    /// <summary>
    /// 认证授权类型
    /// </summary>
    public string ClaimType { internal get; set; }
    /// <summary>
    /// 请求路径
    /// </summary>
    public string LoginPath { get; set; } = "/Api/Login";
    /// <summary>
    /// 发行人
    /// </summary>
    public string Issuer { get; set; }
    /// <summary>
    /// 订阅人
    /// </summary>
    public string Audience { get; set; }
    /// <summary>
    /// 过期时间
    /// </summary>
    public TimeSpan Expiration { get; set; }
    /// <summary>
    /// 签名验证
    /// </summary>
    public SigningCredentials SigningCredentials { get; set; }

    /// <summary>
    /// 构造
    /// </summary>
    /// <param name="deniedAction">拒约请求的url</param>
    /// <param name="permissions">权限集合</param>
    /// <param name="claimType">声明类型</param>
    /// <param name="issuer">发行人</param>
    /// <param name="audience">订阅人</param>
    /// <param name="signingCredentials">签名验证实体</param>
    /// <param name="expiration">过期时间</param>
    public PermissionRequirement(string deniedAction, List<PermissionItem> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials, TimeSpan expiration)
    {
        ClaimType = claimType;
        DeniedAction = deniedAction;
        Permissions = permissions;
        Issuer = issuer;
        Audience = audience;
        Expiration = expiration;
        SigningCredentials = signingCredentials;
    }
}

/// <summary>
/// 用户或角色或其他凭据实体,就像是订单详情一样
/// 之前的名字是 Permission
/// </summary>
public class PermissionItem
{
    /// <summary>
    /// 用户或角色或其他凭据名称
    /// </summary>
    public virtual string Role { get; set; }
    /// <summary>
    /// 请求Url
    /// </summary>
    public virtual string Url { get; set; }
}

三、增加自定义处理类PermissionHandler

 public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
 {
     /// <summary>
     /// 验证方案提供对象
     /// </summary>
     public IAuthenticationSchemeProvider Schemes { get; set; }
     private readonly IHttpContextAccessor _accessor;
     private readonly IUser _user;
     /// <summary>
     /// 构造函数注入
     /// </summary>
     /// <param name="schemes"></param>
     /// <param name="roleModulePermissionServices"></param>
     /// <param name="accessor"></param>
     /// <param name="userServices"></param>
     /// <param name="user"></param>
     public PermissionHandler(IAuthenticationSchemeProvider schemes,
         IHttpContextAccessor accessor,
         IUser user)
     {
         _accessor = accessor;
         _user = user;
         Schemes = schemes;
    
     }

     // 重写异步处理程序 当基于策略授权时触发 [Authorize("Permission")]
     protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
         PermissionRequirement requirement)
     {
         var httpContext = _accessor.HttpContext;
         //验证用户角色 权限等
      }
  }

四、 定义和注册上面的用到的服务   IHttpContextAccessor  、IUser

   // 注册以后可以访问上线问 HttpContext  利用ASP.NET Core提供的IHttpContextAccessor来获取HttpContext的User属性
   services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    //注入NetUser用户接口 HttpContext
   services.AddScoped<IUser, AspNetUser>();
  // 注入自定义权限处理器 用于策略授权验证
   services.AddScoped<IAuthorizationHandler, PermissionHandler>();

五、定义上面的类 AspNetUser

 public class AspNetUser : IUser
 {
     private readonly IHttpContextAccessor _accessor;
     private readonly ILogger<AspNetUser> _logger;

     public AspNetUser(IHttpContextAccessor accessor, ILogger<AspNetUser> logger)
    {
         _accessor = accessor;
         _logger = logger;
     }
     public string Name => GetName();

     private string GetName()
     {
         if (IsAuthenticated() && (_accessor.HttpContext?.User.Identity?.Name??"").IsNotEmptyOrNull())
         {
             return _accessor.HttpContext.User.Identity.Name??"";
         }
         else
         {
             if (!string.IsNullOrEmpty(GetToken()))
             {
                 var getNameType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";
                 return GetUserInfoFromToken(getNameType).FirstOrDefault().ObjToString();
             }
         }
         return "";
     }
     public long ID => GetClaimValueByType("jti").FirstOrDefault().ObjToLong();
     public long TenantId => GetClaimValueByType("TenantId").FirstOrDefault().ObjToLong();
     public bool IsAuthenticated()
     {
         return _accessor.HttpContext?.User?.Identity?.IsAuthenticated ?? false;
     }
     public string GetToken()
     {
         var token = _accessor.HttpContext?.Request?.Headers["Authorization"].ObjToString().Replace("Bearer ", "");
         if (!token.IsNullOrEmpty())
         {
             return token;
         }

         if (_accessor.HttpContext?.IsSuccessSwagger() == true)
         {
             token = _accessor.HttpContext.GetSuccessSwaggerJwt();
             if (token.IsNotEmptyOrNull())
             {
                 if (_accessor.HttpContext.User.Claims.Any(s => s.Type == JwtRegisteredClaimNames.Jti))
                 {
                     return token;
                 }

                 var claims = new ClaimsIdentity(GetClaimsIdentity(token));
                 _accessor.HttpContext.User.AddIdentity(claims);
                 return token;
             }
         }

         return token;
     }
  }

六 、定义封装策略配置

 public static void AddAuthorizationSetup(this IServiceCollection services)
 {
     if (services == null) throw new ArgumentNullException(nameof(services));
     #region 参数
     //读取配置文件
     var symmetricKeyAsBase64 = AppSettings.app(new string[] { "Audience", "Secret" });
     var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
     var signingKey = new SymmetricSecurityKey(keyByteArray);
     var Issuer = AppSettings.app(new string[] { "Audience", "Issuer" });
     var Audience = AppSettings.app(new string[] { "Audience", "Audience" });

     var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);

     // 如果要数据库动态绑定,这里先留个空,后边处理器里动态赋值
     var permission = new List<PermissionItem>();

     // 角色与接口的权限要求参数
     var permissionRequirement = new PermissionRequirement(
         "/api/denied",// 拒绝授权的跳转地址(目前无用)
         permission,
         ClaimTypes.Role,//基于角色的授权
         Issuer,//发行人
         Audience,//听众
         signingCredentials,//签名凭据
         expiration: TimeSpan.FromSeconds(60 * 60)//接口的过期时间
         );
     #endregion
     // 3、自定义复杂的策略授权
     services.AddAuthorization(options =>
     {
         options.AddPolicy("Permissions",
                  policy => policy.Requirements.Add(permissionRequirement));
     });

     services.AddSingleton(permissionRequirement);
 }

七、调用策略配置 Program.cs

builder.Services.AddAuthorizationSetup();