首页 > 其他分享 >一种基于token 和 Permission 的权限管理中间件示例

一种基于token 和 Permission 的权限管理中间件示例

时间:2023-05-20 10:22:07浏览次数:39  
标签:string Permission 示例 中间件 token tokenHelper new using public

1. 先上封装后的使用效果

        [Permission(Key = "/User/AddUser")]
        [HttpPost]
        public Result AddUser([FromBody] SaUser user)
        {
            //Do sth.
            throw new NotImplementedException();
        }
     [Authorize]
        [HttpPost]
        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 <TUser>: 管理用户登录后的token,并根据token 获取登录用户信息。TUser 是LoginUser 的子类。 

namespace WebUtils
{
    public interface  ITokenHelper<TUser>  where TUser :LoginUser 
    {
        public void AddToken(string token, TUser user);
        public void RemoveToken(string token);
        public TUser GetLoginUser (string token);
    }
}

 

    TokenHelper 是 ITokenHelper<LoginUser> 的默认实现,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[token];
            }
            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<AuthenticationSchemeOptions>
    {
        private ITokenHelper<LoginUser> _tokenHelper;
        public PermissionAuthenticationHandler(ITokenHelper<LoginUser> tokenHelper, IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
            this._tokenHelper = tokenHelper;
        }
        public static string CustomerSchemeName = "Permission";
        protected override Task<AuthenticateResult> 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
{
    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)]
    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

 

标签:string,Permission,示例,中间件,token,tokenHelper,new,using,public
From: https://www.cnblogs.com/NewBigLiang/p/17416825.html

相关文章

  • Docker容器安装示例(nginx、redis、nacos、oracle)
    1.nginx示例1.创建容器1.查看是否有nginx镜像dockerimages2.如果没有镜像,可以搜索镜像dockersearchnginx3.指定版本拉取nginxdockerpullnginx:1.20.04.查看镜像dockerimages5.创建容器(-d后台运行,-p容器80端口映射到宿主机8080端口,指定名称nginx-test,指定镜像ID:......
  • [rarcrack] you don't have a right permissions!
    https://blog.csdn.net/l1028386804/article/details/84504217https://blog.csdn.net/The_IT_Crowd/article/details/7672676备忘用,老是忘记怎么暴力破解压缩包,这个错误加两个--就行。juwan@juwan-n85-dls:~$rarcrackSchool_Days.rar-threads4-typerarRarCrack!0.2......
  • XAF EFCore 示例
    前言在DEV官方建议创建新的XAF项目推荐选择EFCore时,我也第一时间创建了XAF的EFCore项目,这也是我第一次创建这个类型的项目,之前一直使用XPO,避免不了要对比一下。如果熟悉XPO但不了解EFCore的小伙伴来说,会有些迷茫,再加上通过向导创建的项目并不是开箱即用,还需要进行配置,可能会打击......
  • AdventureWorks2008R2示例数据字典
    表1:人力资源.雇员——HumanResources.Employee 键字段类型空属性引用字段说明1主键BusinessEntityIDint非空 Person.Person雇员记录主键,外键:BusinessEntity.BusinessEntityID。2惟一NationalIDNumbernvarchar(15)非空  唯一的国民识别号码,如社......
  • 【示例】性能场景设计
    参考https://www.cnblogs.com/uncleyong/p/15475614.html 环境为了演示简单,这里使用的是非微服务架构,思路是通用的 单场景重点是目标、加压方式等   混合场景(容量场景)重点是容量目标、单业务目标、业务比例、加压方式等  稳定性场景重点是目标业务量......
  • 使用 SAP Fiori Tools 提供的中间件导入本地下载的 SAP UI5 库文件来启动 SAP UI5 应
    本教程第100个步骤,笔者介绍了如何使用本地部署的SAPUI5库文件来运行SAPUI5应用的技巧:SAPUI5应用开发教程之一百-如何修改SAPUI5框架的源代码实现,以及使用本地部署的SAPUI5SDK我们简单回顾一下这种方法的实现步骤:从SAPUI5官网将我们想使用的SAPUI5SDK......
  • 消息中间件-RabbitMQ
    网络协议。基于TCP上面架构更高层次的功能框架。这里主要是异步,中间服务器,多个客户端角色。多对多的情形。发布--订阅模式Mqtt—messagequeueingtelemetrytransport发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)就是消息分了类型,然后指定某个类型接收队列模......
  • SAP Fiori Tools 里 proxy 中间件的使用场景介绍
    使用proxy中间件可以让开发人员通过配置的方式连接不同的后台系统,或者是切换不同的SAPUI5版本。下面是一个典型的ui5.yaml里的proxy配置文件的例子:-name:fiori-tools-proxyafterMiddleware:compressionconfiguration:backend:-path:/sapur......
  • .net 中间件
    广义上来讲:Tomcat、WebLogic、Redis、IIS都是中间件;狭义上来讲,也就是基于.NETCORE技术本身来说,中间件指的是ASP.NETCore中的一个组件。中间件是ASP.NETCore的核心组件,MVC框架、响应缓存,身份验证、Cors、Swagger等都是内置中间件。中间件由前逻辑,next,后逻辑3部分组成,前逻辑为......
  • Java通过反射获取Fields、Method、Constructor示例
    1.getFields()和getDeclaredFields()getFields能获取该类和父类(包括Object)public的属性,getDeclaredFields获取该类public和private的属性2.getMethods()和getDeclaredMethods()getMethods能获取该类和父类(包括Object)public的方法,getDeclaredMethods获取该类public和privat......