Asp.net mvc 具有自定义身份验证的自定义授权属性

Asp.net mvc 具有自定义身份验证的自定义授权属性,asp.net-mvc,authorize-attribute,custom-authentication,Asp.net Mvc,Authorize Attribute,Custom Authentication,我使用ASP.NETMVC4Web应用程序作为一些WCF服务的前端。 所有用户登录/注销和会话控制都在后端完成。MVC应用程序应该只存储一个会话ID为的cookie。我的客户端不允许使用表单身份验证,所有内容都必须自定义 我已在我的web.config中设置了以下内容: <system.web> ... <authentication mode="None" /> </system.web> <system.webServer>

我使用ASP.NETMVC4Web应用程序作为一些WCF服务的前端。 所有用户登录/注销和会话控制都在后端完成。MVC应用程序应该只存储一个会话ID为的cookie。我的客户端不允许使用表单身份验证,所有内容都必须自定义

我已在我的web.config中设置了以下内容:

  <system.web>
...
    <authentication mode="None" />
  </system.web>

  <system.webServer>
    <modules>
...
      <remove name="FormsAuthentication" />
...    
    </modules>
  </system.webServer>
在Global.asax中称为

   FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
我已经用[AllowAnonymous]标记了每个不需要授权的控制器和操作

现在我必须实现MyAuthorizeAttribute。我试过一些教程,但没有一个完全符合我的场景

基本上,我必须为每个操作处理以下场景:

  • 如果存在有效的cookie,则应考虑当前请求 已授权(将没有任何要检查的角色,只有一种用户)
  • 如果没有饼干,我应该吃 覆盖默认MVC处理程序(尝试加载帐户/登录) 并将用户重定向到主页/索引页,其中包含用户 应该登录
  • 如果WCF方法调用抛出 FaultException,其中我们的自定义SecurityFault表示 该会话已过期(SecurityFault具有自定义枚举字段) 其中包含例外的原因),我应该破坏我的习惯 会话cookie,然后再次使用 用户应登录的消息,因为他的最后一个会话已结束 期满。对于所有其他安全故障,我可以让它们通过-我 有一个全局错误处理程序
  • 据我所知,我需要覆盖AuthorizeCore(检查我的cookie以查看会话是否存在并且仍然有效)和HandleUnauthorizedRequest(将用户重定向到主页/索引,而不是默认登录页面)

    对于重定向,我尝试了:

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {            
            base.HandleUnauthorizedRequest(filterContext);
            filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
        }
    
    这似乎可以处理第二种情况(不过我不确定是否需要基本调用)

    对于第一个场景,我需要实现AuthorizeCore。我不知道如何正确地做。我已经看到AuthorizeAttribute有一些处理缓存情况的代码,可能还有更多隐藏的功能,我不想破坏它


    对于第三种情况,我不确定MyAuthorizeAttribute是否能够处理它。AuthorizationAttribute是否可以捕获操作内部发生的异常,或者我必须在全局错误处理程序中处理SecurityFault.SessionExpired情况?

    不完全确定我是否能获得它,但如果您创建一个自定义授权筛选器,该筛选器继承自System.Web.MVC.Authorization属性,如下所示

        public class CustomAuthorize : AuthorizeAttribute
        {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (CookieIsValid(filterContext.Request.Cookies["cookieyouwant"])
            {
                 filterContext.Result = new RedirectResult("DestUrl");
            }
            else
            {
                filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
            }
        }
    }
    

    然后装饰您需要使用此授权的方法,以实现此目的?

    以下是我目前的做法:

      public class MyAuthorizeAttribute : AuthorizeAttribute
        {
            public override void OnAuthorization(AuthorizationContext filterContext)
            {
                bool authorized = false;
    
                /// MVC 4 boilerplate code follows
                if (filterContext == null)
                    throw new ArgumentNullException("filterContext");
    
                bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                              || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);
    
                if (skipAuthorization)
                {
                    return;
                }
    
                if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
                {
                    throw new InvalidOperationException(
                        "MyAuthorizeAttribute cannot be used within a child action caching block."
                    );
                }
                // end of MVC code
    
    
                // custom code
                if (!AuthorizeCore(filterContext.HttpContext))
                {
                    // if not authorized from some other Action call, let's try extracting user data from custom encrypted cookie
                    var identity = MyEncryptedCookieHelper.GetFrontendIdentity(filterContext.HttpContext.Request);
                    // identity might be null if cookie not received
                    if (identity == null)
                    {
                        filterContext.HttpContext.User = new GenericPrincipal(new GenericIdentity(""), null);
                    }
                    else
                    {
                        authorized = true;
                        filterContext.HttpContext.User = new MyFrontendPrincipal(identity);
                    }
    
                    // make sure the Principal's are in sync - there might be situations when they are not!
                    Thread.CurrentPrincipal = filterContext.HttpContext.User;
                }
    
                // MVC 4 boilerplate code follows
                if (authorized)
                {
                    // ** IMPORTANT **
                    // Since we're performing authorization at the action level, the authorization code runs
                    // after the output caching module. In the worst case this could allow an authorized user
                    // to cause the page to be cached, then an unauthorized user would later be served the
                    // cached page. We work around this by telling proxies not to cache the sensitive page,
                    // then we hook our custom authorization code into the caching mechanism so that we have
                    // the final say on whether a page should be served from the cache.
    
                    HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                    cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                    cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
                }
                else
                {
                    HandleUnauthorizedRequest(filterContext);
                }
                //end of MVC code
            }
    
            protected override bool AuthorizeCore(HttpContextBase httpContext)
            {
                if (httpContext == null)
                    throw new ArgumentNullException("httpContext");
    
                // check to make sure the user is authenticated as my custom identity
                var principal = httpContext.User as MyFrontendPrincipal;
                if (principal == null)
                    return false;
    
                var identity = principal.Identity as MyFrontendIdentity;
                if (identity == null)
                    return false;
    
                return true;
            }
    
            protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
            {            
                // default MVC result was:
                // filterContext.Result = new HttpUnauthorizedResult();
    
                // but I redirect to index login page instead of kicking 401
                filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
            }
    
            // MVC 4 boilerplate code follows
            private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
            {
                validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
            }
    
            // This method must be thread-safe since it is called by the caching module.
            protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
            {
                if (httpContext == null)
                    throw new ArgumentNullException("httpContext");
    
                bool isAuthorized = AuthorizeCore(httpContext);
                return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
            }
        } 
    

    但是,它不能处理我的第三个场景,因此我将在全局错误处理程序中实现它。

    关于您的第一个需求:

    正如您已经发现的,
    OnAuthorization
    处理了许多方面,包括缓存等。
    如果您只对自定义验证用户凭据的方式感兴趣,我建议您改为覆盖
    AuthorizeCore
    。例如:

    public class ClientCookieAuthorizeAttribute : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            HttpCookie cookie = httpContext.Request.Cookies[_tokenCookieName];
    
            bool isAuthenticated = ValidateUserByCookie(cookie);
    
            return isAuthenticated;
        }
    
        private bool ValidateUserByCookie(HttpCookie cookie)
        {
            var result = false;
            // Perform validation
            // You could include httpContext as well, to check further information
            return result;
        }
    
        private static const string _tokenCookieName = "myCookieName";
    }
    
    您可能还想看看其他线程:


  • 谢谢,我刚刚查看了MVC4源代码,现在我发现它没有我想象的那么复杂,AuthorizeAttribute只有一堆代码行,所以我可以将自己的部分放到需要的地方。我关心的是缓存问题,所以我只是从defaultauthorizeAttribute复制了相关的代码。
    public class ClientCookieAuthorizeAttribute : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            HttpCookie cookie = httpContext.Request.Cookies[_tokenCookieName];
    
            bool isAuthenticated = ValidateUserByCookie(cookie);
    
            return isAuthenticated;
        }
    
        private bool ValidateUserByCookie(HttpCookie cookie)
        {
            var result = false;
            // Perform validation
            // You could include httpContext as well, to check further information
            return result;
        }
    
        private static const string _tokenCookieName = "myCookieName";
    }