计海龄 发表于 2025-6-6 19:51:09

asp.net core 一种基于token 和 Permission 的权限管理Filter 过滤器

asp.net core webapi 下面,想做一个过滤权限的Filter,配合token,对api做一个较为细粒度的权限控制,
该filter (PermissionFilter) 的作用是用户LoginUser.Permissions 列表中有 Key指定的权限才可以访问,没有则返回403 错误码。
 
1. 先上封装后的使用效果
      
      
      public Result AddUser( SaUser user)
      {
            //Do sth.
            throw new NotImplementedException();
      }<br>     
      
      public Result<UserInfoDto> GetUserInfo()
      {
             //Do sth.
      }    说明:要求登录即可,不要求特定权限的,可以使用【Authroize】 attribute 标记,
  要求 特定权限 如   "/User/AddUser" 的 ,使用 【Permission】特性标记,使用Key指定需要的权限。 没有登录的返回401, 没有权限的返回403.
 
2. 实现。主要类及接口说明:
    LoginUser : 登录用户,包含用户基础信息,权限等。可以继承此类封装更多信息。
namespace WebUtils
{
    public class LoginUser
    {
      public string EnterpriseId { get; set; }
      public string UserName { get; set;}

      public string Token { get; set; }

      public DateTime LoginTime { get; set;}
      /// <summary>
      /// 可用权限
      /// </summary>
      public HashSet<string> Permissions { get; set;}
    }

    ITokenHelper : 管理用户登录后的token,并根据token 获取登录用户信息。TUser 是LoginUser 的子类。 
namespace WebUtils
{
    public interfaceITokenHelper<TUser>where TUser :LoginUser
    {
      public void AddToken(string token, TUser user);
      public void RemoveToken(string token);
      public TUser GetLoginUser (string token);
    }

    TokenHelper 是 ITokenHelper 的默认实现,LoginUser 和Token 存内存中,进程重启会丢失。实际应用可以封装自己的实现,把信息持久化到数据库或者Redis 中。
namespace WebUtils
{
    public class TokenHelper : ITokenHelper<LoginUser>
    {
      
      private Dictionary<string, LoginUser> UserDic = new Dictionary<string, LoginUser>();
      
      public void AddToken(string token, LoginUser au)
      {
            UserDic.Add(token, au);
      }
         

      public LoginUser GetLoginUser(string token)
      {
            if (UserDic.ContainsKey(token))
            {
                return UserDic;
            }
            return null;
      }

      public void RemoveToken(string token)
      {
            if (UserDic.ContainsKey(token))
            {
                UserDic.Remove(token);
            }
      }
    }
}View Code 
    PermissionAuthenticationHandler:检查请求是否携带token,并检查TokenHelper 中是否包含此token.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System;
using System.Net;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using WebUtils;

namespace WebUtils
{
    public class PermissionAuthenticationHandler : AuthenticationHandler
    {
      private ITokenHelper<LoginUser> _tokenHelper;
      public PermissionAuthenticationHandler(ITokenHelper<LoginUser> tokenHelper, IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
            : base(options, logger, encoder, clock)
      {
            this._tokenHelper = tokenHelper;
      }
      public static string CustomerSchemeName = "Permission";
      protected override Task HandleAuthenticateAsync()
      {
            AuthenticateResult result;
            Context.Request.Headers.TryGetValue("Authorization", out StringValues values);
            string token = values.ToString();
            if (!string.IsNullOrWhiteSpace(token))
            {
                var loginInfo = _tokenHelper.GetLoginUser(token);

                if (loginInfo == null)
                  result = AuthenticateResult.Fail("未登陆");
                else
                {
                  var claimsIdentity = new ClaimsIdentity(new Claim[]
                        {
                              new Claim(ClaimTypes.Name, loginInfo.UserName),
                              new Claim(ClaimHelper.EnterpriseId,loginInfo.EnterpriseId),
                              new Claim(ClaimHelper.Token, loginInfo.Token)
                        }, CustomerSchemeName);
                  var principal = new ClaimsPrincipal(claimsIdentity);


                  AuthenticationTicket ticket = new AuthenticationTicket(principal, Scheme.Name);

                  result = AuthenticateResult.Success(ticket);
                }
            }
            else
            {
                result = AuthenticateResult.Fail("未登陆");
            }
            return Task.FromResult(result);
      }
    }
}View Code 
    PermissionAttribute: 继承自 Attribute,IFilterFactory ,返回真正的IAuthorizationFilter实例。
    DonotUsePermissionFilterAttribute 继承自 Attribute, IAuthorizationFilter 检查是否拥有指定的权限。
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WebUtils
{
   
    public class PermissionAttribute : Attribute,IFilterFactory
    {
      public string Key { get; set; }

      public bool IsReusable => false;

      public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
      {
            var instance= serviceProvider.GetService<DonotUsePermissionFilterAttribute>();
            instance.Key = this.Key;
            return instance;
      }
    }

    /// <summary>
    /// 防止用户直接调用,起名DonotUse,
    /// </summary>
    public class DonotUsePermissionFilterAttribute : Attribute, IAuthorizationFilter
    {
      private ITokenHelper<LoginUser> _tokenHelper;
      public DonotUsePermissionFilterAttribute(ITokenHelper<LoginUser> tokenHelper)
      {
            this._tokenHelper = tokenHelper;
      }
      public string Key { get; set; }

      public void OnAuthorization(AuthorizationFilterContext context)
      {
            var token = context.HttpContext.User?.GetValue(ClaimHelper.Token);
            if (token == null)
            {
                context.Result = new ObjectResult("用户未登录") { StatusCode = 401 };
                return;
            }
            var user = _tokenHelper.GetLoginUser(token);
            if (user == null)
            {
                context.Result = new ObjectResult("用户token 已失效") { StatusCode = 401 };
                return;
            }
            if (!user.Permissions.Contains(Key))
            {
                context.Result = new ObjectResult("鉴权失败,请联系管理员授权!") { StatusCode = 403 };
                return;
            }
      }
    }
}View Code 
    
    PermissionMiddleWare 把相关实例和PermissionAuthenticationHandler添加到Service 中。
using Microsoft.Extensions.DependencyInjection;

namespace WebUtils
{
    public static class PermissionMiddleWare
    {
      /// <summary>
      /// 基于token和permission 的权限认证中间件
      /// </summary>
      /// <param name="services"></param>
      /// <param name="TokenHelperType"></param>
      /// <returns></returns>
      public static IServiceCollection AddPermission(this IServiceCollection services,Type TokenHelperType)
      {
            services.AddSingleton(typeof(ITokenHelper<LoginUser>), TokenHelperType);

            services.AddTransient(typeof(PermissionAttribute));
            services.AddTransient(typeof(DonotUsePermissionFilterAttribute));
            services.AddAuthentication(o =>
            {
                o.DefaultAuthenticateScheme = PermissionAuthenticationHandler.CustomerSchemeName;
                o.DefaultChallengeScheme = PermissionAuthenticationHandler.CustomerSchemeName;
                o.AddScheme<PermissionAuthenticationHandler>(PermissionAuthenticationHandler.CustomerSchemeName, PermissionAuthenticationHandler.CustomerSchemeName);
            });


            return services;
      }
    }
}View Code 3. 在program.cs 中调用
  在原来添加AddAuthorization 的地方换成下面这句
builder.Services.AddPermission(typeof(TokenHelper));  别忘了后面use
            app.UseAuthentication();
            app.UseAuthorization(); 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: asp.net core 一种基于token 和 Permission 的权限管理Filter 过滤器