6

我有一个 Web 表单应用程序,我正在尝试使用新的 Web API 测试版。我公开的端点应该只对站点的经过身份验证的用户可用,因为它们是供 AJAX 使用的。在我的 web.config 中,我将其设置为拒绝所有用户,除非他们经过身份验证。这与 Web 表单一样有效,但不适用于 MVC 或 Web API。

我已经创建了一个 MVC 控制器和 Web API 控制器来进行测试。我看到的是,在我进行身份验证之前,我无法访问 MVC 或 Web API 端点,但随后我可以继续访问这些端点,即使在关闭浏览器并回收应用程序池之后也是如此。但是,如果我点击了我的一个 aspx 页面,它会将我发送回我的登录页面,那么在我再次进行身份验证之前,我无法点击 MVC 或 Web API 端点。

会话失效后,MVC 和 Web API 无法像我的 ASPX 页面那样运行是否有原因?从外观上看,只有 ASPX 请求正在清除我的表单身份验证 cookie,我假设这是这里的问题。

4

3 回答 3

3

如果您的 Web API 只是在现有的 MVC 应用程序中使用,我的建议是AuthorizeAttribute为您的 MVC 和 WebApi 控制器创建一个自定义过滤器;我创建了一个我称之为“AuthorizeSafe”的过滤器,它默认将所有内容列入黑名单,这样如果您忘记将授权属性应用于控制器或方法,您将被拒绝访问(我认为默认的白名单方法是不安全的)。

提供了两个属性类供您扩展;System.Web.Mvc.AuthorizeAttributeSystem.Web.Http.AuthorizeAttribute; 前者与 MVC 表单身份验证一起使用,后者也与表单身份验证挂钩(这非常好,因为这意味着您不必为 API 身份验证和授权构建一个完全独立的身份验证架构)。这就是我想出的 - 默认情况下它拒绝访问所有 MVC 控制器/动作和 WebApi 控制器/动作,除非应用了AllowAnonymousorAuthorizeSafe属性。首先,一个帮助自定义属性的扩展方法:

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;
    }
}

AuthorizeAttribute两个扩展都使用的授权助手类:

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,
}

两个扩展类本身:

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;
        }
    }
}

最后,可以应用于方法/控制器以允许特定角色的用户访问它们的属性:

public class AuthorizeSafeAttribute : Attribute {
    public string Roles { get; set; }
}

然后我们从 Global.asax 全局注册我们的“AuthorizeSafe”过滤器:

    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());
    }

然后打开一个动作到例如。匿名访问或仅管理员访问:

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" }
        };
    }
}
于 2012-11-12T14:52:02.963 回答
1

它应该在普通 MVC 控制器中工作。你只需要用 [Authorize] 属性来装饰动作。

在 web api 中,您需要具有自定义授权。您可能会发现以下链接很有帮助。

http://www.codeproject.com/Tips/376810/ASP-NET-WEB-API-Custom-Authorize-and-Exception-Han

于 2012-05-04T05:27:50.343 回答
-1

如果您使用的是 MVC Authorize 属性,它对 WebAPI 的工作方式应该与普通 MVC 控制器的工作方式相同。

于 2012-04-26T10:39:14.690 回答