欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-17 08:47:03
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)
    ;

实现效果

访问原来的匿名接口

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

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

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

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

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

userid 为0访问需要登录的接口
asp.net core 自定义 Policy 替换 AllowAnonymous 的行为

userid 大于0访问需要登录的接口
asp.net core 自定义 Policy 替换 AllowAnonymous 的行为

more

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

reference