Custom AuthorizeAttribute with custom authentication

后端 未结 3 1241
生来不讨喜
生来不讨喜 2020-12-31 13:34

I am using ASP.NET MVC 4 Web application as a front-end for some WCF services. All the user log in/log out and session control is done on the back-end. MVC app should only

相关标签:
3条回答
  • 2020-12-31 13:53

    Here is how I did it for now:

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

    It does not handle my 3rd scenario, though, so I'll implement it in a global error handler.

    0 讨论(0)
  • 2020-12-31 13:56

    Regarding your first requirement:

    As you already found out, OnAuthorization takes care of a number of aspects, including e.g. caching.
    If you are only interested in customizing the way in which user credentials are validated, I suggest you go for overriding AuthorizeCore instead. E.g.:

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

    You might also want to give a look at this other threads:

    1. SO - Custom Authorize Attribute
    2. ASP.NET - Custom AuthorizationFilter redirect problems
    3. Diary of a ninja
    0 讨论(0)
  • 2020-12-31 14:06

    Not totally sure I get it but if you create an Custom Authorization Filter that inherits from System.Web.MVC.Authorize attribute like this.

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

    And then decorate your Methods that need to employ this Authorization will that do the trick?

    0 讨论(0)
提交回复
热议问题