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")
public class AllowAnonymousPolicyTransformer : IApplicationModelConvention { private readonly string _policyName; public AllowAnonymousPolicyTransformer() : this("anonymous") { } public AllowAnonymousPolicyTransformer(string policyName) => _policyName = policyName; public void Apply(ApplicationModel application) { foreach (var controllerModel in application.Controllers) { if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AuthorizeFilter))) { foreach (var actionModel in controllerModel.Actions) { if (actionModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter))) { var allowAnonymousFilter = actionModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter)); actionModel.Filters.Remove(allowAnonymousFilter); actionModel.Filters.Add(new AuthorizeFilter(_policyName)); } } } else { if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter))) { var allowAnonymousFilter = controllerModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter)); controllerModel.Filters.Remove(allowAnonymousFilter); } controllerModel.Filters.Add(new AuthorizeFilter(_policyName)); } } } } public static class MvcBuilderExtensions { public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder) { builder.Services.Configure<MvcOptions>(options => { options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer()); }); return builder; } public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder, string policyName) { builder.Services.Configure<MvcOptions>(options => { options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer(policyName)); }); return builder; } }
controller 中的代码:
[