Programmatically add [AllowAnonymous] attribute to all my controller methods

删除回忆录丶 提交于 2020-01-25 08:47:06

问题


I have a WebAPI2 REST api with several controllers. I use role based authentication. I put [Authorize] attributes on all my controllers and some methods. However, in the DEV environment I want to disable authentication. I was hoping that I can put some code into the WebApiConfig such as:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

        //==============================================
        // NOTE: disable all authentication in the DEV_ENVIRONMENT

        if (Environment.GetEnvironmentVariable("DEV_ENVIRONMENT") == "1")
        {
            config.Filters.Add(new AllowAnonymousAttribute());
        }
    }
}

However, this does not compile, because

error CS1503: Argument 1: cannot convert from 
'System.Web.Http.AllowAnonymousAttribute' to 
'System.Web.Http.Filters.IFilter'

Is there a way at runtime to turn off all authentication in my REST api?


回答1:


I wouldn't 'remove' authorization. Suppose you have a customer and CustomerId is a claim, then you can't test the code because claims are missing. Instead I would choose to add an identity for development purposes.

It's probably a hack, but my strategy would be to add a filter where the current user is set including the required roles(s):

using System.Security.Principal;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

public class AddIdentityFilter : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        HttpContext.Current.User = new GenericPrincipal(new GenericIdentity("John"), new[] { "Admin" });
        base.OnAuthorization(actionContext);
    }
}

In WebApiConfig.cs add the filter:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.SuppressDefaultHostAuthentication();

        // Add some logic here to determine the environment
        var isDevelopment = true;

        if (isDevelopment)
            config.Filters.Add(new AddIdentityFilter());

        // ...
    }

This way you can define multiple scenarios for testing while developing.


And here's a similar approach for an asp.net core 3.0 app. In Startup.Configure:

if (env.IsDevelopment())
{
    // Custom authentication
    app.Use(async (context, next) =>
    {
        // Set claims for the test user.
        var claims = new[] { new Claim("role", "Admin"), new Claim("sub", "some guid") };
        var id = new ClaimsIdentity(claims, "DebugAuthorizationMiddleware", "name", "role");
        // Add the test user as Identity.
        context.User.AddIdentity(id);
        // User is now authenticated.
        await next.Invoke();
    });
}
else
{
    // use configured authentication
    app.UseAuthentication();
}
app.UseAuthorization();



回答2:


I will share a couple of ways to do this. The way my services work, we have a base controller class FooController that has all the logic in it for service. Per environment (please don't ask!) we have DevFooController that derives from FooController.

In the Register method we have something that looks a lot like this:

var controller = (Environment.GetEnvironmentVariable("DEV_ENVIRONMENT") == "1") ? "DevFoo" : "Foo";

/// api/{tenant}/{id}
config.Routes.MapHttpRoute(
      name: "RouteName",
      routeTemplate: "api/{tenant}/{id}",
      defaults: new { controller = controller, action = "actionName" });

The attributes are applied at the corresponding controller.

Hokey? Yup. Works? Also Yup.

A different system I used to work on used dependency injection. All controllers were always registered. On each request the injector had some juicy bits about the request (dev/prod, flights, geo, etc.) and was able to select the correct concrete controller. The classes looked similar but FooController also implemented IFooController and the multiple registered class were all available simultaneously vs. the above example where only one staticly configured route is available.

I would prefer the IoC approach but I wasn't around when the service I support now was being drawn up on the whiteboard.

One notable feature we also implement this way is CORS support. It's not available on any of the pre-production endpoints.

Finally we have an ActionFilter on several of our methods that might work like you want. The "allow anonymous" logic is in the ActionFilter itself. If your condition is true the filter just continues without verifying any identity. We perform our own AuthZ but can configure it like you describe.

Hope one of these suggestions can work for you.



来源:https://stackoverflow.com/questions/53234135/programmatically-add-allowanonymous-attribute-to-all-my-controller-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!