我正在尝试从满足一系列标准的一组用户中保护我的MVC路由。由于MVC似乎使用了很多属性而Steven Sanderson在他的专业MVC书中使用了一个安全可扩展性,所以我开始沿着这条路走下去,但我想根据我应用它的动作来定义规则。< / p>
有些行为只适用于员工,有些则不适用。
有些行为仅适用于公司1,有些则不适用。
所以我在想这种用法......
[DisableAccess(BlockUsersWhere = u => u.Company != "Acme")]
public ActionResult AcmeOnlyAction()
{
...
}
[DisableAccess(BlockUsersWhere = u => u.IsEmployee == false)]
public ActionResult EmployeeOnlyAction()
{
...
}
对我来说看起来很干净,并且实现起来非常简单,但是我收到以下编译器错误:
'BlockUsersWhere'不是有效的命名属性参数,因为它不是有效的属性参数类型
显然你不能使用Func作为属性参数。有什么其他建议可以解决这个问题或其他提供我们在MVC项目中喜欢的简单用法的建议吗?
答案 0 :(得分:4)
Necros的建议会起作用,但是你必须在每个动作方法的主体中调用他的SecurityGuard
助手。
如果您仍想使用基于声明属性的方法(其优点是可以将属性应用于整个Controller),您可以编写自己的AuthorizeAttribute
public class CustomAuthorizeAttribute : AuthorizeAttribute {
public bool EmployeeOnly { get; set; }
private string _company;
public string Company {
get { return _company; }
set { _company = value; }
}
protected override bool AuthorizeCore(HttpContextBase httpContext) {
return base.AuthorizeCore(httpContext) && MyAuthorizationCheck(httpContext);
}
private bool MyAuthorizationCheck(HttpContextBase httpContext) {
IPrincipal user = httpContext.User;
if (EmployeeOnly && !VerifyUserIsEmployee(user)) {
return false;
}
if (!String.IsNullOrEmpty(Company) && !VerifyUserIsInCompany(user)) {
return false;
}
return true;
}
private bool VerifyUserIsInCompany(IPrincipal user) {
// your check here
}
private bool VerifyUserIsEmployee(IPrincipal user) {
// your check here
}
}
然后你会按如下方式使用它
[CustomAuthorize(Company = "Acme")]
public ActionResult AcmeOnlyAction()
{
...
}
[CustomAuthorize(EmployeeOnly = true)]
public ActionResult EmployeeOnlyAction()
{
...
}
答案 1 :(得分:1)
由于你只能在属性参数中使用常量,类型或数组初始化器,它们可能不会这样做,或者至少不会那么灵活。
或者,您可以使用我在解决此问题时提出的类似内容。
这是API:
public static class SecurityGuard
{
private const string ExceptionText = "Permission denied.";
public static bool Require(Action<ISecurityExpression> action)
{
var expression = new SecurityExpressionBuilder();
action.Invoke(expression);
return expression.Eval();
}
public static bool RequireOne(Action<ISecurityExpression> action)
{
var expression = new SecurityExpressionBuilder();
action.Invoke(expression);
return expression.EvalAny();
}
public static void ExcpetionIf(Action<ISecurityExpression> action)
{
var expression = new SecurityExpressionBuilder();
action.Invoke(expression);
if(expression.Eval())
{
throw new SecurityException(ExceptionText);
}
}
}
public interface ISecurityExpression
{
ISecurityExpression UserWorksForCompany(string company);
ISecurityExpression IsTrue(bool expression);
}
然后创建表达式构建器:
public class SecurityExpressionBuilder : ISecurityExpression
{
private readonly List<SecurityExpression> _expressions;
public SecurityExpressionBuilder()
{
_expressions = new List<SecurityExpression>();
}
public ISecurityExpression UserWorksForCompany(string company)
{
var expression = new CompanySecurityExpression(company);
_expressions.Add(expression);
return this;
}
public ISecurityExpression IsTrue(bool expr)
{
var expression = new BooleanSecurityExpression(expr);
_expressions.Add(expression);
return this;
}
public bool Eval()
{
return _expressions.All(e => e.Eval());
}
public bool EvalAny()
{
return _expressions.Any(e => e.Eval());
}
}
实施安全表达式:
internal abstract class SecurityExpression
{
public abstract bool Eval();
}
internal class BooleanSecurityExpression : SecurityExpression
{
private readonly bool _result;
public BooleanSecurityExpression(bool expression)
{
_result = expression;
}
public override bool Eval()
{
return _result;
}
}
internal class CompanySecurityExpression : SecurityExpression
{
private readonly string _company;
public CompanySecurityExpression(string company)
{
_company = company;
}
public override bool Eval()
{
return (WhereverYouGetUser).Company == company;
}
}
您可以根据需要添加任意数量的自定义表达式。基础设施有点复杂,但用法非常简单:
public ActionResult AcmeOnlyAction()
{
SecurityGuard.ExceptionIf(s => s.UserWorksForCompany("Acme"));
}
您也可以链接表达式,并将其用作示例中的条件(使用SecurityGuard.Require()
)。
Sry for long post,希望这有帮助。