asp.net core 自定义 Policy 替换 AllowAnonymous 的行为
程序员文章站
2023-11-18 13:02:04
asp.net core 自定义 Policy 替换 AllowAnonymous 的行为 Intro 最近对我们的服务进行了改造,原本内部服务在内部可以匿名调用,现在增加了限制,通过 identity server 来管理 api 和 client,网关和需要访问api的客户端或api服务相互调用 ......
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 中的代码:
[route("api/[controller]")] public class valuescontroller : controller { private readonly ilogger _logger; public valuescontroller(ilogger<valuescontroller> logger) { _logger = logger; } // get api/values [httpget] public actionresult<ienumerable<string>> get() { var msg = $"isauthenticated: {user.identity.isauthenticated} ,username: {user.identity.name}"; _logger.loginformation(msg); return new string[] { msg }; } // get api/values/5 [authorize] [httpget("{id:int}")] public actionresult<string> get(int id) { return "value"; } // ... }
startup 中 configureservices 配置:
var anonymouspolicyname = "anonymous"; services.addauthorization(options => { options.addpolicy(anonymouspolicyname, builder => builder.requireassertion(context => context.user.identity.isauthenticated)); options.defaultpolicy = new authorizationpolicybuilder(headerauthenticationdefaults.authenticationschema) .requireauthenticateduser() .requireassertion(context => context.user.getuserid<int>() > 0) .build(); }); services.addmvc(options => { options.conventions.add(new apicontrollerversionconvention()); }) .addanonymouspolicytransformer(anonymouspolicyname) ;
实现效果
访问原来的匿名接口
userid 为0访问原来的匿名接口
userid 大于0访问原来的匿名接口
userid 为0访问需要登录的接口
userid 大于0访问需要登录的接口
more
注:按照上面的做法已经可以做到自定义 policy 代替 allowanonymous 的行为,但是原来返回的401,现在可能返回到就是 403 了