Getting values from Custom AuthorizeAttribute being default

匆匆过客 提交于 2021-02-08 11:36:54

问题


I have the follow Custom AuthorizeAttribute:

public class SystemAuthorizeAttribute : AuthorizeAttribute
{
   public Form PermissionForm { get; set; } //Enum

   public PermissionValue Permissions { get; set; }//Enum

   public override void OnAuthorization(AuthorizationContext filterContext)
   {                
      //Request is an Authenticated Method?
      if (filterContext.HttpContext.Request.IsAuthenticated)
      {
                Debug.WriteLine("Test 1 " + PermissionForm);
           if (!CurrentUserHasPermissionForm(PermissionForm))
          {
              //Deny access code
          }
      }
   }
  //...
}

After Login method it redirects to Index page from HomeController. The problem is when use SystemAuthorize Attribute in my HomeController the Form value always come as 0 when it should be 4 (Content).

HomeController method:

[SystemAuthorize(PermissionForm = Form.CONTENT, Permissions = PermissionValue.VIEW)]
public ActionResult Index()
{
    return this.View();
}

Login method:

[AllowAnonymous]
[Route("Account/Login")]
public ActionResult Login(LoginViewModel model, string url = "")
{
   var user= GetUserAccount(model);
   if (user == null)
   {
     ModelState.AddModelError("", "User not found!");
     return View(model);
   }
   else
   {
       FormsAuthentication.SetAuthCookie(user.Sign, false);

       var authTicket = new FormsAuthenticationTicket(1, user.Sign, DateTime.Now, DateTime.Now.AddMinutes(20), false, JsonConvert.SerializeObject(user));

       var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authTicket));
                HttpContext.Response.Cookies.Add(authCookie);

                return RedirectToAction("Index", "Home");
    }
}

Form enum:

 public enum Form : short
    {
        PATIENT = 1,
        USERS = 2,
        MEDICE = 3,
        CONTENT = 4,
    }

What I'm doing wrong or missing?


回答1:


Unfortunately Microsoft made this a bit confusing by combining IAuthorizationFilter with Attribute in the same class. The fact of the matter is that attributes cannot do anything except store meta-data.

The part of MVC that reads the attribute is the IAuthorizationFilter which through some MVC magic is registered with MVC automatically when you place AuthorizeAttribute (or a subclass) on a controller or action.

But the only way to actually read the meta-data from the attribute is to use Reflection. The meta-data is in the same class, but not the same instance of the class. The meta-data is in the Attribute, but the code that executes when the filter runs is in the IAuthorizationFilter, which is a separate instance of the same class.

public class SystemAuthorizeAttribute : AuthorizeAttribute
{
    public Form PermissionForm { get; set; } //Enum

    public PermissionValue Permissions { get; set; }//Enum

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var actionDescriptor = httpContext.Items["ActionDescriptor"] as ActionDescriptor;
        if (actionDescriptor != null)
        {
            var authorizeAttribute = this.GetSystemAuthorizeAttribute(actionDescriptor);

            // If the authorization attribute exists
            if (authorizeAttribute != null)
            {
                // Run the authorization based on the attribute
                var form = authorizeAttribute.PermissionForm;
                var permissions = authorizeAttribute.Permissions;

                // Return true if access is allowed, false if not...
                if (!CurrentUserHasPermissionForm(form))
                {
                    //Deny access code
                }
            }
        }

        return true;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // Pass the current action descriptor to the AuthorizeCore
        // method on the same thread by using HttpContext.Items
        filterContext.HttpContext.Items["ActionDescriptor"] = filterContext.ActionDescriptor;
        base.OnAuthorization(filterContext);
    }

    private SystemAuthorizeAttribute GetSystemAuthorizeAttribute(ActionDescriptor actionDescriptor)
    {
        SystemAuthorizeAttribute result = null;

        // Check if the attribute exists on the action method
        result = (SystemAuthorizeAttribute)actionDescriptor
            .GetCustomAttributes(attributeType: typeof(SystemAuthorizeAttribute), inherit: true)
            .SingleOrDefault();

        if (result != null)
        {
            return result;
        }

        // Check if the attribute exists on the controller
        result = (SystemAuthorizeAttribute)actionDescriptor
            .ControllerDescriptor
            .GetCustomAttributes(attributeType: typeof(SystemAuthorizeAttribute), inherit: true)
            .SingleOrDefault();

        return result;
    }
}

Note that OnAuthorization has some logic in it that you will need to support output caching and the part of the code that checks for [AllowAnonymous], so you should not put your authorization check there, but in AuthorizeCore. But unfortunately, AuthorizeCore isn't passed the ActionDescriptor you need to check whether the attribute exists, so you need the above httpContext.Items hack to ensure it is passed into that method on the same thread.

The Reflection part becomes much more clear if you separate your Attribute into a different class from the IAuthorizationFilter, as in this example.



来源:https://stackoverflow.com/questions/46587828/getting-values-from-custom-authorizeattribute-being-default

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!