在Web API中使用表单身份验证

时间:2012-04-26 00:50:04

标签: forms-authentication webforms asp.net-web-api

我有一个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,我假设这是问题。

3 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:1)

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

在web api中,您需要拥有自定义授权。您可以在下面找到有用的链接。

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

答案 2 :(得分:-1)

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