欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

asp.net Core 中AuthorizationHandler 实现自定义授权

程序员文章站 2022-05-18 22:45:23
前言 ASP.NET Core 中 继承的是AuthorizationHandler ,而ASP.NET Framework 中继承的是AuthorizeAttribute. 它们都是用过重写里面的方法实现过滤请求的。 现在我们实现如何在 ASP.NET Core MVC 实现自定义授权。 关于Au ......

前言

asp.net core 中 继承的是authorizationhandler ,而asp.net framework 中继承的是authorizeattribute.

它们都是用过重写里面的方法实现过滤请求的。 

现在我们实现如何在 asp.net core mvc 实现自定义授权。

关于authorizationhandler 详细介绍可以看这里

如何自定义授权

比如我们后台有个博客管理功能,那我们可以新建一个blog的控制器,比如blogcontroller

里面有添加,删除,编辑等功能,分别是add,delete,edit

代码如下

 public class blogcontroller : controller
    {
        public iactionresult index()
        {
            return view();
        }
        /// <summary>
        /// 博客添加页面
        /// </summary>
        /// <returns></returns>
        public iactionresult add()
        {
            return view();
        }
        /// <summary>
        /// 博客列表页面
        /// </summary>
        public iactionresult list()
        {
            return view();
        }
        /// <summary>
        /// 博客编辑页面
        /// </summary>
        public iactionresult edit()
        {
            return view();
        }
    }

如果有打印可以起个名字叫  public iactionresult print()

自定义就是做个控制界面做勾选功能,用户根据自身业务选择。

asp.net Core 中AuthorizationHandler 实现自定义授权

以此类推,在asp.net 框架下默认路由就是controller和action,除非你修改默认路由,当然了你修改默认路由你的权限逻辑也得变。

实现过滤器

authorizationhandler 参数里面有个iauthorizationrequirement要我们去填充,根据我们自己业务自己选择定义数据。

 public class permissionrequirement : iauthorizationrequirement
    {
        /// <summary>
        /// 无权限action
        /// </summary>
        public string deniedaction { get; set; } = "/home/visitdeny";

        /// <summary>
        /// 认证授权类型
        /// </summary>
        public string claimtype { internal get; set; }
        /// <summary>
        /// 默认登录页面
        /// </summary>
        public string loginpath { get; set; } = "/home/login";
        /// <summary>
        /// 过期时间
        /// </summary>
        public timespan expiration { get; set; }
        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="deniedaction"></param>
        /// <param name="claimtype"></param>
        /// <param name="expiration"></param>
        public permissionrequirement(string deniedaction, string claimtype, timespan expiration)
        {
            claimtype = claimtype;
            deniedaction = deniedaction;
            expiration = expiration;
        }
    }

第一个参数集合

 public class permissionitem
    {
        /// <summary>
        /// 用户或角色或其他凭据名称
        /// </summary>
        public virtual string role { get; set; }
        /// <summary>
        /// 配置的controller名称
        /// </summary>
        public virtual string controllername { get; set; }
        /// <summary>
        /// 配置的action名称
        /// </summary>
        public virtual string actionname { get; set; }
    }

 

startup 里面,添加一个授权策略,permissionrequirement 放进去,然后注入

           ////权限要求参数
            var permissionrequirement = new permissionrequirement(
                "/home/visitdeny",// 拒绝授权的跳转地址
                claimtypes.name,//基于用户名的授权
                expiration: timespan.fromseconds(60 * 5)//接口的过期时间
                );
            #endregion

            //【授权】
            services.addauthorization(options =>
            {
                options.addpolicy("permission", policy => policy.requirements.add(permissionrequirement));
            });
            // 注入权限处理器
            services.addtransient<iauthorizationhandler, permissionhandler>();

控制器里面加上标示

 [authorize("permission")]
 public class blogcontroller : controller
{
}

 

登录页面授权

 [httppost]
        public async task<iactionresult> login(loginviewmodel model)
        {
            if (modelstate.isvalid)
            {
                if (model.textuser == null)
                {
                    modelstate.addmodelerror("", "请输入账号.");
                    return view(model);
                }
                if (model.textpassword == null)
                {
                    modelstate.addmodelerror("", "请输入密码.");
                    return view(model);
                }
                if (model.textuser == "admin"  && model.textpassword == "123")
                {
                    #region 传统的登录  
                    //只判断是否登录  通过[authorize] 小项目中只有一个管理员 只要账号和密码对就行
                    var claimidentity = new claimsidentity(cookieauthenticationdefaults.authenticationscheme);
                    claimidentity.addclaim(new claim(claimtypes.name, model.textuser));

                    var claimsprincipal = new claimsprincipal(claimidentity);
                    //await httpcontext.signinasync(claimsprincipal);
                    await httpcontext.signinasync(cookieauthenticationdefaults.authenticationscheme, claimsprincipal);
                    #endregion

                    //下面代码是演示的,实际项目要从根据用户名或者角色从数据库读取出来 配置到 list<permissionitem>里面
                    //这里我用的是用户名判断的,根据自己的业务自己处理
                    //测试的时候 可以 删除一条记录试试,或者添加一条
                    list<permissionitem> lsperm = new list<permissionitem>();
                    lsperm.add(new permissionitem() { role = model.textuser, controllername = "blog", actionname = "add" });//添加博客页面的权限
                    lsperm.add(new permissionitem() { role = model.textuser, controllername = "blog", actionname = "edit" });//编辑博客页面的权限
                    lsperm.add(new permissionitem() { role = model.textuser, controllername = "blog", actionname = "list" });//查看博客页面的权限
                    string perdata = jsonconvert.serializeobject(lsperm);
                    await _cacheservice.setstringasync("perm" + model.textuser, perdata);
                    return redirecttoaction("index", "home");
                }
            }
            return view(model);
        }

list<permissionitem> 我用redis存储的,大家根据实际情况存储。

权限判断

 public class permissionhandler : authorizationhandler<permissionrequirement>
    {
        public iauthenticationschemeprovider schemes;
        readonly idistributedcache _cacheservice;
        /// <summary>
        /// 构造函数注入
        /// </summary>
        public permissionhandler(iauthenticationschemeprovider schemes, idistributedcache cacheservice)
        {
            schemes = schemes;
            _cacheservice = cacheservice;
        }

        // 重载异步处理程序
        protected override async task handlerequirementasync(authorizationhandlercontext context, permissionrequirement requirement)
        {
            //从authorizationhandlercontext转成httpcontext,以便取出表求信息
            authorizationfiltercontext filtercontext = context.resource as authorizationfiltercontext;
            httpcontext httpcontext = filtercontext.httpcontext;
            authenticateresult result = await httpcontext.authenticateasync(schemes.getdefaultauthenticateschemeasync().result.name);
            //如果没登录result.succeeded为false
            if (result.succeeded)
            {
                httpcontext.user = result.principal;
                //当前访问的controller
                string controllername = filtercontext.routedata.values["controller"].tostring();//通过actioncontext类的routedata属性获取controller的名称:home
                //当前访问的action
                string actionname = filtercontext.routedata.values["action"].tostring();//通过actioncontext类的routedata属性获取action的名称:index
                string name = httpcontext.user.claims.singleordefault(s => s.type == claimtypes.name)?.value;
                string perdata = await _cacheservice.getstringasync("perm" + name);
                list<permissionitem> lst = jsonconvert.deserializeobject<list<permissionitem>>(perdata);
                if (lst.where(w => w.controllername == controllername && w.actionname == actionname).count() > 0)
                {
                    //如果在配置的权限表里正常走
                    context.succeed(requirement);
                }
                else
                {
                    //不在权限配置表里 做错误提示
                    //如果是ajax请求 (包含了vue等 的ajax)
                    string requesttype = filtercontext.httpcontext.request.headers["x-requested-with"];
                    if (!string.isnullorempty(requesttype) && requesttype.equals("xmlhttprequest", stringcomparison.currentcultureignorecase))
                    {
                        //ajax 的错误返回
                        //filtercontext.result = new statuscoderesult(499); //自定义错误号 ajax请求错误 可以用来错没有权限判断 也可以不写 用默认的
                        context.fail();
                    }
                    else
                    {
                        //普通页面错误提示 就是跳转一个页面
                        //httpcontext.response.redirect("/home/visitdeny");//第一种方式跳转
                        filtercontext.result = new redirecttoactionresult("visitdeny", "home", null);//第二种方式跳转
                        context.fail();
                    }
                }
            }
            else
            {
                context.fail();
            }
        }
    }

至此我们实现定义授权判断。实际业务上每个人可以根据自己的情况做处理。