【问题标题】:ASP.NET MVC4 custom authorizationASP.NET MVC4 自定义授权
【发布时间】:2014-01-08 19:02:42
【问题描述】:

我已经在 ASP.NET MVC4 中实现了自定义授权属性,方法是重写 AuthorizeAttribute 来进行自定义授权。我想在某些控制器和控制器操作上实现此功能,以使它们拒绝未经授权的请求,例如未登录的用户和/或某些权限问题。我不想实现 asp.net 成员资格和授权,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Diagnostics.CodeAnalysis;
using System.Security.Principal;
using System.Web.Mvc.Properties;
using System.Web.Routing;

namespace TestMVC4
{
    public sealed class AuthorizeUserAttribute : AuthorizeAttribute
    {
        private static readonly string[] _emptyArray = new string[0];
        string activities;
        string[] activitiesList = _emptyArray;

        string roles;
        string[] rolesList = new string[0];

        /// <summary>
        /// Gets or sets list of authorized activities 
        /// </summary>
        public string Activities
        {
            get { return activities ?? String.Empty; }
            set
            {
                activities = value;
                activitiesList = SplitString(value);
            }
         }

         public string CRole
         {
            get { return roles ?? String.Empty; }
            set
            {
                 roles = value;
                 rolesList = SplitString(value);
             }
         }

        /// <summary>
        /// Authorization initial call
        /// </summary>
        /// <param name="filterContext">Filter context</param>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
            {
                // If a child action cache block is active, we need to fail immediately, even if authorization
                // would have succeeded. The reason is that there's no way to hook a callback to rerun
                // authorization before the fragment is served from the cache, so we can't guarantee that this
                // filter will be re-run on subsequent requests.
                //throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
                throw new InvalidOperationException("Cannot use within child action cache.");
            }

            bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                                     || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

            if (skipAuthorization)
            {
                return;
            }

            if (AuthorizeCore(filterContext.HttpContext))
            {
                // ** 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);
            }
        }

        /// <summary>
        /// Main method for overriding custom authorization for application
        /// </summary>
        /// <param name="httpContext">Current execution context</param>
        /// <returns>True/False whether user is authorized for given activity or not respectively</returns>
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }

            //Verify sessionuser roles
            if (rolesList != null)
            {
                String sessionRole = Convert.ToString(httpContext.Session["MyRole"]);

                foreach (String role in rolesList)
                {
                    if (role == sessionRole)
                        return true;
                }
            }

            //IPrincipal user = httpContext.User;
            //if (!user.Identity.IsAuthenticated)
            //{
            //    return false;
            //}

            //if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
            //{
            //    return false;
            //}

            //if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
            //{
            //    return false;
            //}

            return false;
        }

        /// <summary>
        /// Overriden method for handling unauthorized request routing
        /// </summary>
        /// <param name="filterContext">Authorization context for which the request has been made</param>
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
            filterContext.Result = new HttpUnauthorizedResult();

            //filterContext.RequestContext.RouteData;

            //filterContext.Result = new RedirectToRouteResult(
            //        new RouteValueDictionary(
            //            new
            //            {
            //                controller = "Error",
            //                action = "Unauthorised"
            //            })
            //        );
        }

        private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
            validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
        }

        protected HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }

            bool isAuthorized = AuthorizeCore(httpContext);
            return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
        }

        /// <summary>
        /// Splits the string on commas and removes any leading/trailing whitespace from each result item.
        /// </summary>
        /// <param name="original">The input string.</param>
        /// <returns>An array of strings parsed from the input <paramref name="original"/> string.</returns>
        string[] SplitString(string original)
        {
            if (String.IsNullOrEmpty(original))
            {
                return _emptyArray;
            }

            var split = from piece in original.Split(',')
                        let trimmed = piece.Trim()
                        where !String.IsNullOrEmpty(trimmed)
                        select trimmed;
            return split.ToArray();
        }
      }
}  

在调用 OnAuthoriztion 方法之前一切正常。问题是我的自定义属性activitiesroles 以及所有其他变量都变为空或无效我不明白为什么会这样。所有这些变量都被初始化,但在到达OnAuthorization 函数时它们都将变为无效。

【问题讨论】:

    标签: asp.net-mvc-4 attributes authorize


    【解决方案1】:

    使用

     Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(userName),
                                    new string[] {"Roles goes here"});
    

    然后用户将登录,您将能够访问用户

    User.Identity.Name
    

    在我的情况下,我有属性

    public class BasicHttpAuthorizeAttribute : AuthorizeAttribute
    {
    
        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            if (Thread.CurrentPrincipal.Identity.Name.Length == 0)
            {
                        if (ValidateUser(userName, password))
                            Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(userName, "Basic"),
                                new string[] {});
    
            }
            return base.IsAuthorized(actionContext);
        }
    }
    

    所以我所有标有该属性的控制器都将使用自定义登录 + 如果我设置 AllowAnonimous 它也将跳过 Auth

    【讨论】:

    • 我只想在初始化 AuthorizeUserAttribute 时设置角色和活动变量,但是当 OnAuthorization 方法调用时我将其值设为 null。我想在我的控制器类顶部启动的 AuthorizationCore 函数中获取活动和角色变量值。
    • @Volodymyr Bilyachat using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace TestMVC4.Controllers { [AuthorizeUser(Activities = "View", CRole = "User")] public class Default1Controller : Controller { // // GET: /Default1/ public ActionResult Index() { return View(); } } }
    • 好吧,给我几分钟,我有点忙,我会尽力帮助你;)
    • @zeeshan 为什么你认为这些值是空的?我刚试过,我可以看到设置值
    • 我只看到您将 MyRole 设置为会话,问题是在您重建解决方案后会话将消失,所以问题可能与会话有关?
    【解决方案2】:

    我自己找到了解决问题的方法,我已应用 AttributeUsage 将行为限制在类方法中。除此之外,我还使用了它的 AllowMultiple 属性为 true,这导致了问题,并使我的变量值重置,因为它在每次执行时都使用新实例来覆盖前一个实例。将此变量设置为 false 即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-01
      • 2013-12-14
      • 2021-03-17
      • 2013-10-31
      • 1970-01-01
      • 2010-09-30
      • 1970-01-01
      相关资源
      最近更新 更多