问题
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