asp.net core 自定义 Policy 替换 AllowAnonymous 的行为

Intro

最近对我们的服务进行了改造,原本内部服务在内部可以匿名调用,现在增加了限制,通过 identity server 来管理 api 和 client,网关和需要访问api的客户端或api服务相互调用通过 client_credencial 的方式来调用,这样一来我们可以清晰知道哪些 api 服务会被哪些 api/client 所调用,而且安全性来说更好。为了保持后端服务的代码更好的兼容性,希望能够实现相同的代码通过在 Startup 里不同的配置实现不同的 Authorization 逻辑,原来我们的服务的 Authorize 都是以 Authorize("policyName") 的形式来写的,这样一来我们只需要修改这个 Policy 的授权配置就可以了。对于 AllowAnonymous 就希望可以通过一种类似的方式来实现,通过自定义一个 Policy 来实现自己的逻辑

实现方式

将 action 上的 AllowAnonymous 替换为 Authorize("policyName"),在没有设置 Authorize 的 controller 上增加 Authorize("policyName")

  1. public class AllowAnonymousPolicyTransformer : IApplicationModelConvention

  2. {

  3. private readonly string _policyName;

  4. public AllowAnonymousPolicyTransformer() : this("anonymous")

  5. {

  6. }

  7. public AllowAnonymousPolicyTransformer(string policyName) => _policyName = policyName;

  8. public void Apply(ApplicationModel application)

  9. {

  10. foreach (var controllerModel in application.Controllers)

  11. {

  12. if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AuthorizeFilter)))

  13. {

  14. foreach (var actionModel in controllerModel.Actions)

  15. {

  16. if (actionModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter)))

  17. {

  18. var allowAnonymousFilter = actionModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter));

  19. actionModel.Filters.Remove(allowAnonymousFilter);

  20. actionModel.Filters.Add(new AuthorizeFilter(_policyName));

  21. }

  22. }

  23. }

  24. else

  25. {

  26. if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter)))

  27. {

  28. var allowAnonymousFilter = controllerModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter));

  29. controllerModel.Filters.Remove(allowAnonymousFilter);

  30. }

  31. controllerModel.Filters.Add(new AuthorizeFilter(_policyName));

  32. }

  33. }

  34. }

  35. }

  36. public static class MvcBuilderExtensions

  37. {

  38. public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder)

  39. {

  40. builder.Services.Configure<MvcOptions>(options =>

  41. {

  42. options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer());

  43. });

  44. return builder;

  45. }

  46. public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder, string policyName)

  47. {

  48. builder.Services.Configure<MvcOptions>(options =>

  49. {

  50. options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer(policyName));

  51. });

  52. return builder;

  53. }

  54. }

controller 中的代码:

  1. [Route("api/[controller]")]

  2. public class ValuesController : Controller

  3. {

  4. private readonly ILogger _logger;

  5. public ValuesController(ILogger<ValuesController> logger)

  6. {

  7. _logger = logger;

  8. }

  9. // GET api/values

  10. [HttpGet]

  11. public ActionResult<IEnumerable<string>> Get()

  12. {

  13. var msg = $"IsAuthenticated: {User.Identity.IsAuthenticated} ,UserName: {User.Identity.Name}";

  14. _logger.LogInformation(msg);

  15. return new string[] { msg };

  16. }

  17. // GET api/values/5

  18. [Authorize]

  19. [HttpGet("{id:int}")]

  20. public ActionResult<string> Get(int id)

  21. {

  22. return "value";

  23. }

  24. // ...

  25. }

Startup 中 ConfigureServices 配置:

  1. var anonymousPolicyName = "anonymous";

  2. services.AddAuthorization(options =>

  3. {

  4. options.AddPolicy(anonymousPolicyName, builder => builder.RequireAssertion(context => context.User.Identity.IsAuthenticated));

  5. options.DefaultPolicy = new AuthorizationPolicyBuilder(HeaderAuthenticationDefaults.AuthenticationSchema)

  6. .RequireAuthenticatedUser()

  7. .RequireAssertion(context => context.User.GetUserId<int>() > 0)

  8. .Build();

  9. });

  10. services.AddMvc(options =>

  11. {

  12. options.Conventions.Add(new ApiControllerVersionConvention());

  13. })

  14. .AddAnonymousPolicyTransformer(anonymousPolicyName)

  15. ;

实现效果

访问原来的匿名接口

userId 为0访问原来的匿名接口

userId 大于0访问原来的匿名接口

userId 为0访问需要登录的接口

userId 大于0访问需要登录的接口

More

注:按照上面的做法已经可以做到自定义 policy 代替 AllowAnonymous 的行为,但是原来返回的401,现在可能返回到就是 403 了

Reference

  • https://github.com/WeihanLi/AspNetCorePlayground/blob/master/TestWebApplication

asp.net core 自定义 Policy 替换 AllowAnonymous 的行为相关推荐

  1. asp.net core 自定义基于 HttpContext 的 Serilog Enricher

    asp.net core 自定义基于 HttpContext 的 Serilog Enricher Intro 通过 HttpContext 我们可以拿到很多有用的信息,比如 Path/QuerySt ...

  2. asp.net core自定义依赖注入容器,替换自带容器

    依赖注入 在asp.net core程序中,众所周知,依赖注入基本上贯穿了整个项目,以通用的结构来讲解,控制器层(Controller层)依赖业务层(Service层),业务层依赖于仓储层(Repos ...

  3. ASP.NET Core自定义响应内容

    问题 在业务开发中,对Web API的返回格式有一定要求,需要是定制化的Json结构,用于前端统一处理: {Status : 0,Message: "",Info : xxx } ...

  4. 解决 ASP.NET Core 自定义错误页面对 Middleware 异常无效的问题

    我们基于 Razor Class Library 实现了自定义错误页面的公用类库(详见之前的随笔),但是在实际使用时发现如果在 middleware 中发生了异常,则不能显示自定义错误页面,而是返回默 ...

  5. asp.net core 自定义 Content-Type

    asp.net core 实现支持自定义 Content-Type Intro 我们最近有一个原本是内网的服务要上公网,在公网上有一层 Cloudflare 作为网站的公网流量提供者,CloudFla ...

  6. asp.net core 自定义异常处理中间件

    Intro 在 asp.net core 中全局异常处理,有时候可能不能满足我们的需要,可能就需要自己自定义一个中间件处理了,最近遇到一个问题,有一些异常,不希望记录错误日志,目前主要是用户请求取消导 ...

  7. asp.net core 自定义401和异常显示内容(JWT认证、Cookie Base认证失败显示内容)

    asp.net core 2.0使用JWT认证园子里已经有挺多帖子了,但开发中发现认证未授权情况下返回的401状态码是没有任何信息的,业务中可能有需要返回一串错误的Json信息.在这里我分享一个自定义 ...

  8. ASP.NET Core 自定义认证方式--请求头认证

    Intro 最近开始真正的实践了一些网关的东西,最近写几篇文章分享一下我的实践以及遇到的问题. 本文主要介绍网关后面的服务如何进行认证. 解决思路 网关可以做一部分的认证和授权,服务内部有时候也会需要 ...

  9. ASP.NET Core 实现基于 ApiKey 的认证

    ASP.NET Core 实现基于 ApiKey 的认证 Intro 之前我们有介绍过实现基于请求头的认证,今天来实现一个基于 ApiKey 的认证方式,使用方式参见下面的示例 Sample 注册认证 ...

最新文章

  1. 享元模式 Flyweight Pattern
  2. 如何在elasticsearch里面使用深度分页功能
  3. flowable画图教程_flowable画图教程_Flowable 学习笔记
  4. Haunt - Youzan 服务发现 概述
  5. Qt C++属性类型提供给 QML调用(二)
  6. MySQL高级 - 索引的使用 - 覆盖索引
  7. MySQL的学习--触发器
  8. 学生命科学要学计算机吗,现在学生物学出路真的有那么不济吗?
  9. WebStorm使用教程
  10. 生成名片二维码 python
  11. idea新建maven项目没有src目录的操作方法
  12. Revit 2011 二次开发之“取得两条直线的交点”
  13. linux gnu ld,GNU LD用法
  14. JavaScript启示录
  15. Jquery项目练习-狂拍灰太狼
  16. 面试之springboot是什么?
  17. module 与 component 的区别
  18. U盘制作-BGA焊接练习
  19. 运用简单C语言代码打印一棵圣诞树
  20. 拉马努金,天才之超越

热门文章

  1. DataForm 中通过外面的按钮进行提交时,出错
  2. Teams bot的调用限制
  3. 网络串流_串流NFL足球的最便宜方式(无需电缆)
  4. Windows下压缩包安装Mysql
  5. informix linux安装步骤
  6. 时任上海来伊份互联网事业群总裁王戈钧 :传统企业(线上+线下)移动互联网改造...
  7. Lintcode165 Merge Two Sorted Lists solution 题解
  8. 2013年7月27日杂记
  9. windows server 2012 dhcp 配置故障转移
  10. window server2008 r2