问题
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