当前位置: 移动技术网 > IT编程>软件设计>设计模式 > asp.net Core 中AuthorizationHandler 实现自定义授权

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

2019年04月11日  | 移动技术网IT编程  | 我要评论

前言

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 框架下默认路由就是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();
            }
        }
    }

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

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网