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 框架下默认路由就是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(); } } }
至此我们实现定义授权判断。实际业务上每个人可以根据自己的情况做处理。
推荐阅读
-
在ASP.NET Core中显示自定义的错误页面
-
在ASP.NET Core中实现自动注入、批量注入
-
如何在Asp.Net Core MVC中处理null值的实现
-
asp.net Core 中AuthorizationHandler 实现自定义授权
-
Asp.Net Core 中IdentityServer4 授权中心之应用实战
-
ASP.NET Core 2.0利用Jwt实现授权认证
-
ASP.NET Core中实现用户登录验证的最低配置示例代码
-
ASP.NET Core中如何实现重定向详解
-
ASP.NET Core实现自定义WebApi模型验证详解
-
Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式