Asp.net mvc 3 在MVC3中按操作方法属性定义身份验证需求
我有一个MVC3应用程序,具有4个身份验证级别和4个基本控制器,每个控制器都有:Asp.net mvc 3 在MVC3中按操作方法属性定义身份验证需求,asp.net-mvc-3,authentication,custom-attributes,Asp.net Mvc 3,Authentication,Custom Attributes,我有一个MVC3应用程序,具有4个身份验证级别和4个基本控制器,每个控制器都有: 未经验证-BaseController 用户-BaseAuthController:BaseController Advisor-BaseAdvisorController:BaseAuthController 管理员-BaseAdminController:BaseAuthController 现在我有一系列针对特殊情况的替代措施。。。e、 通常只供管理员使用的控制器可以有一个或两个操作方法供顾问使用。。。我将覆
BaseController
BaseAuthController:BaseController
BaseAdvisorController:BaseAuthController
BaseAdminController:BaseAuthController
public class BaseAuthController : BaseController
{
/// <summary>
/// Enter action names in here to have them ignored during login detection
/// </summary>
public string[] NoAuthActions = new string[] { };
/// <summary>
/// Actions only usable by Users+
/// </summary>
public string[] UserOnlyActions = new string[] { };
/// <summary>
/// Actions only usable by Advisors+
/// </summary>
public string[] AdvisorOnlyActions = new string[] { };
/// <summary>
/// Actions only usable by Admins+
/// </summary>
public string[] AdminOnlyActions = new string[] { };
.......
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//special code here to determine what to do with requested action...
//verifies that user is logged in and meets requirements for method...
//if not, redirects out to another page...
}
}
这个系统实际上对我们来说运行得很好,但对我来说,问题是它感觉很混乱。您必须在一个位置定义这些方法,必要时在其他位置覆盖它们的身份验证级别。如果您更改了方法名称,您必须记住在其他地方更改它
我希望能够用身份验证特定的属性来修饰方法本身,并去掉基于字符串的定义(或者至少使它们透明并动态地使用列表
或其他东西)。这是我要找的一个例子
[HttpPost]
[AdvisorAuthentication]
public ActionResult GrowthStageSelection(int growerID, int reportGrowthStageID = 0)
{
//code...
}
问题是我找不到一个很好的方法来实现这一点。我曾尝试创建actionfilteratAttribute
的子类,但它们是在我的BaseAuthController
对OnActionExecuting
的重写之后运行的。此时,在游戏中动态地向字符串列表添加新方法已经太晚了,而且我甚至无法从属性访问当前控制器实例
也许这整个想法是错误的。谁能给我指出正确的方向吗?谢谢
最终解决方案
首先,我删除了除BaseController之外的所有特殊控制器——我不再使用它们了。我将当前的特殊身份验证代码从BaseAuthController
移动到BaseController
。接下来,我为每个身份验证状态定义了一系列属性:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class BaseAuthAttribute : Attribute
{
public AuthLevels AuthLevel { get; protected set; }
public BaseAuthAttribute(AuthLevels level)
{
this.AuthLevel = level;
}
public override string ToString()
{
return string.Format("Auth Required: {0}", this.AuthLevel.ToString());
}
}
public class UnauthenticatedAccess : BaseAuthAttribute
{
public UnauthenticatedAccess()
: base(AuthLevels.Unauthenticated)
{
}
}
public class UserAccess : BaseAuthAttribute
{
public UserAccess()
: base(AuthLevels.User)
{
}
}
public class AdvisorAccess : BaseAuthAttribute
{
public AdvisorAccess()
: base(AuthLevels.Advisor)
{
}
}
public class AdminAccess : BaseAuthAttribute
{
public AdminAccess()
: base(AuthLevels.Admin)
{
}
}
然后在myBaseController
中,我修改了OnActionExecuting
以对照属性检查登录用户(如果有)的当前身份验证级别。这比以前干净多了!(注意:SessionUser
和AuthLevels
是我们项目的自定义对象-您不会有这些对象)
您可以创建不同的Authorize属性,扩展如下内容
public sealed class AuthenticateAdvisorAttribute : IAuthorizationFilter, FilterAttribute
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//advisor specific logic goes here
}
}
public sealed class AuthenticateAdminAttribute : IAuthorizationFilter, FilterAttribute
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//admin specific logic goes here
}
}
然后,您可以在需要控制器类/操作的任何地方应用这些属性
作为
如果我正确理解了您的问题,您可以实现自己的自定义属性(而不是授权属性),并且在基本控制器的重写OnActionExecuting中,您可以检索执行方法的自定义属性,并根据定义的自定义属性采取适当的操作。因此,如果某个方法具有[AdvisorAuthentication],则您知道在继续之前需要检查这些凭据 编辑: 我没有一个例子可以给你们指出,因为这是我在我的一个项目中实现的东西。我现在无法访问该代码,但这里有一个概要:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
IEnumerable<MyCustomAttribute> attributes = filterContext.ActionDescriptor.GetCustomAttributes(false).OfType<MyCustomAttribute>();
foreach (MyCustomAttributeobj in attributes)
{
switch(MyCustomAttribute.AttribType){
case MyCustomeAttribute.AdvisorAuthentication:
break;
case MyCustomeAttribute.AdminAuthentication:
break;
}
}
}
受保护的覆盖无效OnActionExecuted(ActionExecutedContext筛选器上下文)
{
base.OnActionExecuted(filterContext);
IEnumerable attributes=filterContext.ActionDescriptor.GetCustomAttributes(false).OfType();
foreach(属性中的MyCustomAttributeobj)
{
开关(MyCustomAttribute.AttribType){
案例MyCustomeAttribute.AdvisorAuthentication:
打破
案例MyCustomeAttribute.AdminAuthentication:
打破
}
}
}
您可以只实现一个自定义属性MyCustomAttribute,并让它接受一个参数,以指示您想要的授权类型。与此类似,属性的使用变成了[MyCustomAttribute(“MyCustomeAttribute.AdminAuthentication”)]如果要将不同的身份验证筛选器创建为属性,为什么要使用basecontroller?basecontroller存储了许多常用功能(属性、方法)在所有不同控制器的应用程序中使用。这真的很重要。
OnAuthorization(AuthorizationContext filterContext)
覆盖了什么?我没有在这个应用程序中使用Forms auth,我想这会有所不同。我们在会话中维护了一个控制身份验证的对象。它与您使用的身份验证无关。这是一个合适的地方,你可以在这里拥有身份验证逻辑。顺便说一句,你可以在这里使用filterContext.HttpContext.Session[“key”]
来访问会话变量。有没有办法从这个方法中获取当前控制器的实例?我必须尝试将其转换为BaseController
。谢谢。这听起来是个很好的解决办法。你能提供或链接到一个次要的例子吗?我没有一个例子,但我已经编辑了我的回复,包括一些关于如何继续的指针。如果你需要更多的澄清,让我知道:)我喜欢这个解决方案!我将在上面发布我的解决方案以供参考。非常感谢!很高兴这有帮助。祝你好运:)
[AdminAccess]
public class GrowerController : BaseController
{
public ActionResult Index()
{
//This method will require admin access (as defined for controller)
return View();
}
[AdvisorAccess]
public ActionResult Landing()
{
//This method is overridden for advisor access or greater
return View();
}
}
public sealed class AuthenticateAdvisorAttribute : IAuthorizationFilter, FilterAttribute
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//advisor specific logic goes here
}
}
public sealed class AuthenticateAdminAttribute : IAuthorizationFilter, FilterAttribute
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//admin specific logic goes here
}
}
[AuthenticateAdmin]
public class AdminController : Controller
{
}
[AuthenticateAdvisor]
public class AdvisorController : Controller
{
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
IEnumerable<MyCustomAttribute> attributes = filterContext.ActionDescriptor.GetCustomAttributes(false).OfType<MyCustomAttribute>();
foreach (MyCustomAttributeobj in attributes)
{
switch(MyCustomAttribute.AttribType){
case MyCustomeAttribute.AdvisorAuthentication:
break;
case MyCustomeAttribute.AdminAuthentication:
break;
}
}
}