经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
asp.net Core 中AuthorizationHandler 实现自定义授权
来源:cnblogs  作者:麦兜很乖  时间:2019/4/11 9:05:17  对本文有异议

前言

ASP.NET Core 中 继承的是AuthorizationHandler ,而ASP.NET Framework 中继承的是AuthorizeAttribute.

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

现在我们实现如何在 ASP.NET Core MVC 实现自定义授权。

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

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2#authorization-handlers

如何自定义授权

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

里面有添加,删除,编辑等功能,分别是Add,Delete,Edit

代码如下

  1. public class BlogController : Controller
  2. {
  3. public IActionResult Index()
  4. {
  5. return View();
  6. }
  7. /// <summary>
  8. /// 博客添加页面
  9. /// </summary>
  10. /// <returns></returns>
  11. public IActionResult Add()
  12. {
  13. return View();
  14. }
  15. /// <summary>
  16. /// 博客列表页面
  17. /// </summary>
  18. public IActionResult List()
  19. {
  20. return View();
  21. }
  22. /// <summary>
  23. /// 博客编辑页面
  24. /// </summary>
  25. public IActionResult Edit()
  26. {
  27. return View();
  28. }
  29. }

如果有打印可以起个名字叫  public IActionResult Print()

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

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

实现过滤器

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

  1. public class PermissionRequirement : IAuthorizationRequirement
  2. {
  3. /// <summary>
  4. /// 无权限action
  5. /// </summary>
  6. public string DeniedAction { get; set; } = "/Home/visitDeny";
  7. /// <summary>
  8. /// 认证授权类型
  9. /// </summary>
  10. public string ClaimType { internal get; set; }
  11. /// <summary>
  12. /// 默认登录页面
  13. /// </summary>
  14. public string LoginPath { get; set; } = "/Home/Login";
  15. /// <summary>
  16. /// 过期时间
  17. /// </summary>
  18. public TimeSpan Expiration { get; set; }
  19. /// <summary>
  20. /// 构造
  21. /// </summary>
  22. /// <param name="deniedAction"></param>
  23. /// <param name="claimType"></param>
  24. /// <param name="expiration"></param>
  25. public PermissionRequirement(string deniedAction, string claimType, TimeSpan expiration)
  26. {
  27. ClaimType = claimType;
  28. DeniedAction = deniedAction;
  29. Expiration = expiration;
  30. }
  31. }

第一个参数集合

  1. public class PermissionItem
  2. {
  3. /// <summary>
  4. /// 用户或角色或其他凭据名称
  5. /// </summary>
  6. public virtual string Role { get; set; }
  7. /// <summary>
  8. /// 配置的Controller名称
  9. /// </summary>
  10. public virtual string controllerName { get; set; }
  11. /// <summary>
  12. /// 配置的Action名称
  13. /// </summary>
  14. public virtual string actionName { get; set; }
  15. }

 

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

  1. ////权限要求参数
  2. var permissionRequirement = new PermissionRequirement(
  3. "/Home/visitDeny",// 拒绝授权的跳转地址
  4. ClaimTypes.Name,//基于用户名的授权
  5. expiration: TimeSpan.FromSeconds(60 * 5)//接口的过期时间
  6. );
  7. #endregion
  8.  
  9. //【授权】
  10. services.AddAuthorization(options =>
  11. {
  12. options.AddPolicy("Permission", policy => policy.Requirements.Add(permissionRequirement));
  13. });
  14. // 注入权限处理器
  15. services.AddTransient<IAuthorizationHandler, PermissionHandler>();

控制器里面加上标示

  1. [Authorize("Permission")]
  2. public class BlogController : Controller
  3. {
  4. }

 

登录页面授权

  1. [HttpPost]
  2. public async Task<IActionResult> Login(LoginViewModel model)
  3. {
  4. if (ModelState.IsValid)
  5. {
  6. if (model.textUser == null)
  7. {
  8. ModelState.AddModelError("", "请输入账号.");
  9. return View(model);
  10. }
  11. if (model.textPassword == null)
  12. {
  13. ModelState.AddModelError("", "请输入密码.");
  14. return View(model);
  15. }
  16. if (model.textUser == "admin" && model.textPassword == "123")
  17. {
  18. #region 传统的登录
  19. //只判断是否登录 通过[Authorize] 小项目中只有一个管理员 只要账号和密码对就行
  20. var claimIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
  21. claimIdentity.AddClaim(new Claim(ClaimTypes.Name, model.textUser));
  22. var claimsPrincipal = new ClaimsPrincipal(claimIdentity);
  23. //await HttpContext.SignInAsync(claimsPrincipal);
  24. await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal);
  25. #endregion
  26.  
  27. //下面代码是演示的,实际项目要从根据用户名或者角色从数据库读取出来 配置到 List<PermissionItem>里面
  28. //这里我用的是用户名判断的,根据自己的业务自己处理
  29. //测试的时候 可以 删除一条记录试试,或者添加一条
  30. List<PermissionItem> lsperm = new List<PermissionItem>();
  31. lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "Add" });//添加博客页面的权限
  32. lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "Edit" });//编辑博客页面的权限
  33. lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "List" });//查看博客页面的权限
  34. string perData = JsonConvert.SerializeObject(lsperm);
  35. await _cacheService.SetStringAsync("perm" + model.textUser, perData);
  36. return RedirectToAction("Index", "Home");
  37. }
  38. }
  39. return View(model);
  40. }

List<PermissionItem> 我用Redis存储的,大家根据实际情况存储。

权限判断

  1. public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
  2. {
  3. public IAuthenticationSchemeProvider Schemes;
  4. readonly IDistributedCache _cacheService;
  5. /// <summary>
  6. /// 构造函数注入
  7. /// </summary>
  8. public PermissionHandler(IAuthenticationSchemeProvider schemes, IDistributedCache cacheService)
  9. {
  10. Schemes = schemes;
  11. _cacheService = cacheService;
  12. }
  13. // 重载异步处理程序
  14. protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
  15. {
  16. //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息
  17. AuthorizationFilterContext filterContext = context.Resource as AuthorizationFilterContext;
  18. HttpContext httpContext = filterContext.HttpContext;
  19. AuthenticateResult result = await httpContext.AuthenticateAsync(Schemes.GetDefaultAuthenticateSchemeAsync().Result.Name);
  20. //如果没登录result.Succeeded为false
  21. if (result.Succeeded)
  22. {
  23. httpContext.User = result.Principal;
  24. //当前访问的Controller
  25. string controllerName = filterContext.RouteData.Values["Controller"].ToString();//通过ActionContext类的RouteData属性获取Controller的名称:Home
  26. //当前访问的Action
  27. string actionName = filterContext.RouteData.Values["Action"].ToString();//通过ActionContext类的RouteData属性获取Action的名称:Index
  28. string name = httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Name)?.Value;
  29. string perData = await _cacheService.GetStringAsync("perm" + name);
  30. List<PermissionItem> lst = JsonConvert.DeserializeObject<List<PermissionItem>>(perData);
  31. if (lst.Where(w => w.controllerName == controllerName && w.actionName == actionName).Count() > 0)
  32. {
  33. //如果在配置的权限表里正常走
  34. context.Succeed(requirement);
  35. }
  36. else
  37. {
  38. //不在权限配置表里 做错误提示
  39. //如果是AJAX请求 (包含了VUE等 的ajax)
  40. string requestType = filterContext.HttpContext.Request.Headers["X-Requested-With"];
  41. if (!string.IsNullOrEmpty(requestType) && requestType.Equals("XMLHttpRequest", StringComparison.CurrentCultureIgnoreCase))
  42. {
  43. //ajax 的错误返回
  44. //filterContext.Result = new StatusCodeResult(499); //自定义错误号 ajax请求错误 可以用来错没有权限判断 也可以不写 用默认的
  45. context.Fail();
  46. }
  47. else
  48. {
  49. //普通页面错误提示 就是跳转一个页面
  50. //httpContext.Response.Redirect("/Home/visitDeny");//第一种方式跳转
  51. filterContext.Result = new RedirectToActionResult("visitDeny", "Home", null);//第二种方式跳转
  52. context.Fail();
  53. }
  54. }
  55. }
  56. else
  57. {
  58. context.Fail();
  59. }
  60. }
  61. }

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

 

原文链接:http://www.cnblogs.com/wangjun8868/p/10683823.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号