首页 文章

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

提问于
浏览
6

我有一个Web窗体应用程序,我正在尝试使用新的Web API测试版 . 我公开的 endpoints 应仅供该网站的经过身份验证的用户使用,因为它们用于AJAX . 在我的web.config中,我设置为拒绝所有用户,除非他们经过身份验证 . 这适用于Web窗体,但无法按预期使用MVC或Web API .

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

有没有理由为什么MVC和Web API无法运行,因为我的ASPX页面一旦我的会话失效了?通过它的外观,只有ASPX请求正在清除我的表单身份验证cookie,我假设这是问题 .

3 回答

  • 3

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

    提供了两个属性类供您扩展; System.Web.Mvc.AuthorizeAttributeSystem.Web.Http.AuthorizeAttribute ;前者与MVC表单身份验证一起使用,后者也与表单身份验证挂钩(这非常好,因为它意味着你不是我想到的 - 它拒绝访问所有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

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

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

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

  • 1

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

相关问题