Se il Web API è solo usato all'interno di un'applicazione MVC esistente, il mio consiglio è quello di creare un filtro personalizzato AuthorizeAttribute
per entrambi i controller MVC e WebAPI; Creo quello che io chiamo un filtro "AuthorizeSafe", che blacklist tutto di default in modo che se si dimentica di applicare un attributo autorizzazione al controller o metodo di accesso, si è negato (penso che l'approccio di default whitelist è insicuro).
Sono disponibili due classi di attributi da estendere; System.Web.Mvc.AuthorizeAttribute
e System.Web.Http.AuthorizeAttribute
; il primo è utilizzato con l'autenticazione forme MVC e quest'ultimo aggancia anche in forme di autenticazione (questo è molto bello perché significa che non c'è bisogno di andare costruire una intera architettura di autenticazione separato per l'autenticazione e l'autorizzazione API). Ecco cosa mi è venuto in mente: nega l'accesso a tutti i controller/azioni MVC e ai controller/azioni WebApi per impostazione predefinita a meno che non venga applicato un attributo o AuthorizeSafe
. In primo luogo, un metodo di estensione per aiutare con attributi personalizzati:
public static class CustomAttributeProviderExtensions {
public static List<T> GetCustomAttributes<T>(this ICustomAttributeProvider provider, bool inherit) where T : Attribute {
List<T> attrs = new List<T>();
foreach (object attr in provider.GetCustomAttributes(typeof(T), false)) {
if (attr is T) {
attrs.Add(attr as T);
}
}
return attrs;
}
}
La classe di autorizzazione di supporto che entrambe le AuthorizeAttribute
estensioni usano:
public static class AuthorizeSafeHelper {
public static AuthActionToTake DoSafeAuthorization(bool anyAllowAnonymousOnAction, bool anyAllowAnonymousOnController, List<AuthorizeSafeAttribute> authorizeSafeOnAction, List<AuthorizeSafeAttribute> authorizeSafeOnController, out string rolesString) {
rolesString = null;
// If AllowAnonymousAttribute applied to action or controller, skip authorization
if (anyAllowAnonymousOnAction || anyAllowAnonymousOnController) {
return AuthActionToTake.SkipAuthorization;
}
bool foundRoles = false;
if (authorizeSafeOnAction.Count > 0) {
AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnAction.First());
foundRoles = true;
rolesString = foundAttr.Roles;
}
else if (authorizeSafeOnController.Count > 0) {
AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnController.First());
foundRoles = true;
rolesString = foundAttr.Roles;
}
if (foundRoles && !string.IsNullOrWhiteSpace(rolesString)) {
// Found valid roles string; use it as our own Roles property and auth normally
return AuthActionToTake.NormalAuthorization;
}
else {
// Didn't find valid roles string; DENY all access by default
return AuthActionToTake.Unauthorized;
}
}
}
public enum AuthActionToTake {
SkipAuthorization,
NormalAuthorization,
Unauthorized,
}
Le due classi di estensione stessi:
public sealed class AuthorizeSafeFilter : System.Web.Mvc.AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext) {
if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) {
throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers.");
}
string rolesString;
AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization(
filterContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0,
filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0,
filterContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false),
filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false),
out rolesString
);
string rolesBackup = this.Roles;
try {
switch (action) {
case AuthActionToTake.SkipAuthorization:
return;
case AuthActionToTake.NormalAuthorization:
this.Roles = rolesString;
base.OnAuthorization(filterContext);
return;
case AuthActionToTake.Unauthorized:
filterContext.Result = new HttpUnauthorizedResult();
return;
}
}
finally {
this.Roles = rolesBackup;
}
}
}
public sealed class AuthorizeSafeApiFilter : System.Web.Http.AuthorizeAttribute {
public override void OnAuthorization(HttpActionContext actionContext) {
if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) {
throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers.");
}
string rolesString;
AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization(
actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0,
actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0,
actionContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(),
actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(),
out rolesString
);
string rolesBackup = this.Roles;
try {
switch (action) {
case AuthActionToTake.SkipAuthorization:
return;
case AuthActionToTake.NormalAuthorization:
this.Roles = rolesString;
base.OnAuthorization(actionContext);
return;
case AuthActionToTake.Unauthorized:
HttpRequestMessage request = actionContext.Request;
actionContext.Response = request.CreateResponse(HttpStatusCode.Unauthorized);
return;
}
}
finally {
this.Roles = rolesBackup;
}
}
}
E infine, l'attributo che può essere applicato a metodi/controller per consentire agli utenti in determinati ruoli di accedervi:
public class AuthorizeSafeAttribute : Attribute {
public string Roles { get; set; }
}
Poi registriamo i nostri filtri "AuthorizeSafe" a livello globale da Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
// Make everything require authorization by default (whitelist approach)
filters.Add(new AuthorizeSafeFilter());
}
public static void RegisterWebApiFilters(HttpFilterCollection filters) {
// Make everything require authorization by default (whitelist approach)
filters.Add(new AuthorizeSafeApiFilter());
}
Poi ad aprire un azione per es.l'accesso anonimo o accesso solo Admin:
public class AccountController : System.Web.Mvc.Controller {
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl) {
// ...
}
}
public class TestApiController : System.Web.Http.ApiController {
// GET API/TestApi
[AuthorizeSafe(Roles="Admin")]
public IEnumerable<TestModel> Get() {
return new TestModel[] {
new TestModel { TestId = 123, TestValue = "Model for ID 123" },
new TestModel { TestId = 234, TestValue = "Model for ID 234" },
new TestModel { TestId = 345, TestValue = "Model for ID 345" }
};
}
}
condividere alcune config, code ... – Aliostad