I'm going to handle authentication and authorization in an action filter and create an action filter like below:
public class Auth : ActionFilterAttribute
{
public int Access { get; set; }
public string Roles { get; set; } = "Default";
public Func<bool> AuthFunc { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
string UserId = HttpContext.Current.User.Identity.GetUserId();
//Authentication
if (Roles != "Default" && UserManager.IsInRole(UserId, Roles))
{
//Authorization
if (AuthFunc) { base.OnActionExecuting(actionContext); }
else
{
var response = actionContext.Request.CreateResponse(HttpStatusCode.Redirect);
Uri requestUrl = actionContext.Request.RequestUri;
response.Headers.Location = new Uri($"{requestUrl.Scheme}://{requestUrl.Host}:{requestUrl.Port}");
actionContext.Response = response;
}
}
else
{
var response = actionContext.Request.CreateResponse(HttpStatusCode.Redirect);
Uri requestUrl = actionContext.Request.RequestUri;
response.Headers.Location = new Uri($"{requestUrl.Scheme}://{requestUrl.Host}:{requestUrl.Port}");
actionContext.Response = response;
}
}
}
And in the controller:
[Auth(Roles="Teacher" , Access = (short)TableEnum.Course , AuthFunc = Courses.CheckCoursesOfTeacher(CourseId))]
public ActionResult ShowExerciseAnswers(int CourseId,int ExerciseId)
{
return View(model: ChapterExerciseAnswer.ExerciseAnswerList(CourseId,ExerciseId));
}
The AuthFunc
method maybe has multiple inputs but just a bool
return value.
How to pass AuthFunc
(the Courses.CheckCoursesOfTeacher(CourseId)
method) to action filter?
How to get CourseId
action parameter in action filter attribute (pass CourseId
or ExerciseId
as an attribute value)?
What is the best way of handling these issues(functions and variables can't be sent to an action filter)?
答案 0 :(得分:0)
我最近发现自己正在寻找这样的解决方案。每个MS Docs的属性参数必须遵循以下规则:
属性构造函数的参数限于简单的类型/文字:bool,int,double,string,Type,enums等以及这些类型的数组。您不能使用表达式或变量。您可以自由使用位置或命名参数。
因此,我们无法通过属性参数将函数传递给过滤器。可能有很多选择,但这是我选择要做的:
我使用依赖项注入向管理器注入了动作过滤器,然后使用 Reflection 告诉过滤器在管理器上执行哪种方法。看起来是这样的:
班级:
public class Phone : AuditBase
{
...other props...
[AuditRuleset(AuditRule.PhoneNumber)]
public string Number { get; set; }
}
枚举:
public enum AuditRule
{
PhoneNumber // You can add [Description] if you want
}
属性:
public class AuditRulesetAttribute : Attribute
{
private readonly AuditRule _rule;
public AuditRulesetAttribute(AuditRule rule) => _rule = rule;
}
过滤器:
public class FormAuditActionFilter : IActionFilter
{
private ILog _log { get; set; }
private IFormAuditor _auditor { get; set; }
public FormAuditActionFilter(ILog log, IFormAuditor auditor)
{
_log = log;
_auditor = auditor;
}
...lots of filter code...
... The following is from OnActionExecuted, having stored the props of the submitted object in objectProperties...
foreach(PropertyInfo propertyInfo in objectProperties)
{
// Check first for any special audit comparison rules which should be followed
var auditRuleAttributes = propertyInfo.CustomAttributes
.Where(x => x.AttributeType.Name == typeof(AuditRulesetAttribute).Name)
.ToList();
if (auditRuleAttributes.Any())
{
IEnumerable<IList<CustomAttributeTypedArgument>> attrList = auditRuleAttributes
.Select(x => x.ConstructorArguments);
foreach(IList<CustomAttributeTypedArgument> attr in attrList)
foreach(CustomAttributeTypedArgument arg in attr)
if (_auditRuleManager.IsChanged(oldValue, newValue, (AuditRule)arg.Value))
result.Add(BuildValueHistory(propertyInfo.Name, oldValue, newValue));
continue;
}
}
...lots more filter code...
}
AuditRuleManager:
public class AuditRuleManager : IAuditRuleManager
{
public bool IsChanged(object val1, object val2, AuditRule rule)
{
object[] objArray = {val1, val2};
var comparisonResult = typeof(AuditRuleManager)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.Single(m => m.Name == rule.GetDescription()) // Try to get description, but falls back to name by default
.Invoke(this, objArray) as bool?;
return (bool)comparisonResult; // Throw an exception if the comparison result was not a valid bool
}
// Compare phone numbers with special rules, and return their equality
private bool PhoneNumber(object val1, object val2) // NOTE: Name of method matches name of enum value
=> GetNumbersFromString(val1 as string) != GetNumbersFromString(val2 as string);
我花了一段时间的最后一块是使用Ninject的过滤器的DI。这就是我的
的工作方式Global.asax.cs:
kernel.BindFilter<FormAuditActionFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<FormAuditAttribute>()
.WithConstructorArgument("log", log)
.WithConstructorArgument("auditor", auditManager);
我没有使用函数作为属性参数,而是使用DI将管理器注入到过滤器中。这使您的过滤器可以访问所需的功能。其次,我使用了一个枚举来保存应执行的函数的名称。因此,从本质上讲,创建新函数并使用参数执行该函数所要做的就是:
我希望这会有所帮助!
https://blogs.cuttingedge.it/steven/posts/2014/dependency-injection-in-attributes-dont-do-it/