『PasteSpider』
最新文章
贴代码出品
热门话题
用户问答
lao人言
「搜索」
【本期话题】更多
三人寄语更多
谋而后动,往往会让你对自己的代码更具信心!
点赞:1
没有最好的语言,只有更合适的语言!
点赞:0
需求就是系统的千年杀,相爱相杀那种!
点赞:0
对于答案来说,更重要的是找到答案的这个过程而不是答案本身!
点赞:0
实际遇到的问题往往在那些视频课程中是不会出现的!
点赞:0
时间与空间总是在换来换去,鱼和熊掌往往不可同得!
点赞:0
逻辑注解清晰的代码优于那些一眼看不明白的语法糖
点赞:0
抛开需求谈架构是无意义的事情!
点赞:0
由于时间的问题,我们往往会给自己埋坑,等着后续或者下一任来填!
点赞:0
测试没问题的不一定没问题,测试有问题的那肯定有问题!
点赞:2
PasteTemplate之接口的授权实现,非JWT方式
尘埃 2024-01-13 751 50 2
RoleAttribute,AuthorizeAttribute,Jwt,Oauth2.0等,PasteSpider的授权机制!

PasteTemplate序列的接口权限控制使用的都是一套逻辑

包括不限于PasteSpider,PasteTimer,PasteTicker等

大致逻辑一致,具体的细节可能会根据项目做一些调整!

实现

项目中使用的是过滤器实现的RoleAttribute:ActionFilterAttribute

查找源代码大概看到如下代码

    /// <summary>
    /// 这里的权限筛选器
    /// </summary>
    public class RoleAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 权限
        /// </summary>
        private string _role { set; get; }

        /// <summary>
        /// 角色
        /// </summary>
        private string _model { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private readonly ModelHelper _appCache;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="appcache"></param>
        /// <param name="Model"></param>
        /// <param name="Role"></param>
        public RoleAttribute(
            ModelHelper appcache,
            string Model = default,
            string Role = default
            )
        {
            _role = Role;
            _model = Model;
            _appCache = appcache;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <exception cref="SmartToolException"></exception>
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var needauth = true;
            foreach (var item in context.Filters)
            {
               // Console.WriteLine(item.ToString()); 可以看看有多少过滤器被注入了
                if (item is AllowAnonymousAttribute)
                {
                    needauth = false;
                    break;
                }
                if (item is RoleAttribute)
                {
                    needauth = (item == this);
                }
            }
            if (needauth)
            {
                //authorization
                if (context.HttpContext.Request.Headers[PublicString.TokenHeadName].Count == 0)
                {
                    throw new SmartToolException("当前未登录,请登录后重试", "401");
                }
                var token = context.HttpContext.Request.Headers[PublicString.TokenHeadName].ToString();
                if (token.StartsWith("Bearer"))
                {
                    token = token.Replace("Bearer ", "");
                }

                //// 获得当前用户信息后 如果要使用base.CurrentUser.的属性,下面要按需注入
                //var list = new System.Security.Claims.ClaimsIdentity();
                //list.AddClaim(new System.Security.Claims.Claim("editionid", "张三"));//EditionId 
                //list.AddClaim(new System.Security.Claims.Claim("client_id", token)); //ClientId
                //list.AddClaim(new System.Security.Claims.Claim("tenantid", token));//TenantId 
                //list.AddClaim(new System.Security.Claims.Claim(ClaimTypes.Name, token));//TenantId 
                //var _guid_userid = Guid.NewGuid().ToString();
                //Console.WriteLine(_guid_userid);
                //list.AddClaim(new System.Security.Claims.Claim(AbpClaimTypes.UserId, _guid_userid));
                //context.HttpContext.User = new System.Security.Claims.ClaimsPrincipal(list);

                //这里可以做token的安全校验
                if (!String.IsNullOrEmpty(token))
                {
                    //if (token.CheckToken())
                    //{
                    var back = _appCache.HasRole(_model, _role, token);
                    if (!back.role)
                    {
                        throw new SmartToolException($"{(back.code == 401 ? "当前登录密钥失效,请重新登录" : "没有当前接口的操作权限,请确认")}", $"{back.code}");
                    }
                    //}
                    //else
                    //{
                    //    throw new SmartToolException("当前密钥信息有误,请登录后重试", "401");
                    //}
                }
                else
                {
                    throw new SmartToolException("当前密钥信息有误,请登录后重试", "401");
                }
            }
            base.OnActionExecuting(context);
        }
    }

注入

在需要使用的地方使用如下方式注入

    [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "add" })]

当然为了省事你也可以使用全局注入,比如

            //全局处理异常信息
            context.Services.AddControllers(options =>
            {
                //访问限制
                //options.Filters.Add(typeof(ReferrerAttribute));
                //错误统一处理
                //options.Filters.Add(typeof(PlanExceptionAttribute));
                //统一授权
                options.Filters.Add(typeof(RoleAttribute));
            });

不过一般不建议全局注入,如果全局注入就变成权限的划分要按照Controller和Action两个部分了,如果使用*做识别,还是有搞头的,

不需要的再通过过滤器AllowAnonymousAttribute标记下,还是可以的!

在PasteTemplate项目中,使用的是按需注入,主要是接口权限的问题,看RoleAttribute可以了解到有2个参数Model和Role

可以这么理解一个是模块,一个是权限,比如会员模块的查看,会员模块的新增!

查看上面信息已经可以知道大概的授权流程了,就是用户访问接口的时候,带上自己的token,

接口处标注model和role,然后代码计算当前token所表示的信息是否包含对应的model和role的权限,有则通过,没有则报403错误!

那么问题就剩下token的生成规则和使用注意问题了!

借鉴jwt或者oauth2.0,个人感觉oauth2.0是更合理的,jwt就有点老赖的感觉,我授权出去了就和我没关系了,每次请求还得附带一大堆东西(权限列表加密后的内容等)

密钥生成

拿PasteSpider的token来说 token的格式大概为1760192342_11923_xoifsdkfwejrkwetjoweirwerwet

从_拆分打开分成三部分1.时间戳 2.当前会员id.3消息加密校验,也可以成为sign

用户登陆后,代码可以获取到当前登陆用户的userid,然后var temptoken=md5/sha/sha256...(timestamp_userid_appsecret);

var token =timestamp_userid_temptoken

这样生成的好处是,在不执行redis查询的时候就可以排除一些加的token的访问

如果对接口的权限授权有区分的,比如get的只做一层校验即可,参考jwt,授权一个密钥使用1个月,那么这个账号下一秒删除了,之前授权出去的还能继续使用直到到期!post等请求对数据有更改的再执行redis的二次确认!这样的做法可以极大的提交相应速度!

其实如果权限不是很多的,可以基于上面的方式参考jwt直接封装到token中也是可以的!消息长度绝对比jwt的短!

密钥作废

如果一个用户被禁用了,只要把它对应的token从redis中删除即可!超过时间戳的,密钥直接作废,其实就是下一次访问的时候服务端检查到密钥过期了,直接返回401

问题一

如何在使用自定义过滤器的同时,使用Abp的base.CurrentUser获取当前登陆用户的信息,这个可以去查看下源码,找到这个base.CurrentUser的具体是如何获取值的就行了,按照他们的关系,在过滤器的时候对对应的值赋值即可!

问题二

如何使用AuthorizeAttribute?有时候人的习惯就是那么奇怪,或者旧项目,只要不启用这个Authorize模块即可,然后随便全局注入一个自己的过滤器,在自己的过滤器里面去找当前是否有AuthorizeAttribute即可实现授权。

问题三

市面上一大堆介绍jwt等的授权,用这个自定义的会不会有并发不足等的问题,这个你得自己研究下代码,看看啥叫瓶颈!!!

评论列表
此男洧⒉
    /// <summary>
    /// 
    /// </summary>
    public class MyAuthFilter : IAuthorizationFilter
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            Console.WriteLine("MyAuth.OnAuthorization");
            //if (context.HttpContext.Request.Headers[PublicString.TokenHeadName].Count == 0)
            //{
            //    throw new SmartToolException("当前未登录,请登录后重试", "401");
            //}

            var info = new ExceptionModel();
            info.code = "401";
            info.success = false;
            info.message = "没有登陆,请登陆后再试!";

            context.Result = new ContentResult
            {
                StatusCode = 401,
                ContentType = "application/json;charset=utf-8",
                Content = Newtonsoft.Json.JsonConvert.SerializeObject(info)
            };
            //context.HttpContext.
        }
    }
配角而已,何必入戏
楼上不错,使用IAuthorizationFilter会更合适些,如果有多个过滤器的话,使用ActionFilterAttribute的方式还要考虑前后,所以还是使用IAuthorizationFilter合适
尘埃
50 751 2
快捷注册
热门推荐更多
PasteTemplate案例项目
贴代码框架的项目案例,里面有PasteForm的案例代码等;
最新动态
  • 61.****.12 正在查看 PasteSpider之--路由列表-私有仓库-环境配置-的介绍 !
  • 46.****.41 正在查看 PasteSpider升级服务器上的一个服务或集群服务 !
  • 19.****.24 正在查看 Serilog在appsettings.json中的配置 !
  • 204.****.193 正在查看 开发者专用Linux容器部署工具PasteSpider(K8S,Jenkins,CICD)介绍 !
  • 116.****.190 正在查看 Redis的安装 !
  • 47.****.127 正在查看 PasteTimer软件介绍 !
  • 133.****.23 正在查看 Serilog在appsettings.json中的配置 !
  • 216.****.30 正在查看 贴代码框架PasteForm之VS2022右键代码生成器插件介绍 !
  • 117.****.55 正在查看 【PasteForm】最佳CRUD的实现案例项目PasteTemplate解析,包含源码(一) !
  • 116.****.60 正在查看 贴代码框架PasteForm特性介绍之markdown和richtext !
  • 37.****.215 正在查看 Redis的安装 !
欢迎加入QQ讨论群 296245685 更新记录 [PasteSpider]介绍 @2022-2023 PasteCode.cn 版权所有 ICP证 闽ICP备2021013869号-2