FunctionExprValidator 用于验证查询中函数表达式的使用是否符合既定的安全策略。
当使用表达式扩展功能注册自定义函数时,可能需要限制这些函数的使用范围,防止滥用或安全风险。FunctionExprValidator 提供了一种机制来控制哪些函数表达式可以在查询中使用。
public enum FunctionPolicy
{
AllowAll, // 允许所有函数表达式
AllowRegisted, // 仅允许预注册的函数表达式
Disallow // 不允许任何函数表达式
}
| 策略 | 说明 |
|---|---|
AllowAll |
允许任何函数表达式,包括未注册的 |
AllowRegisted |
仅允许通过 SqlBuilder.RegisterFunctionSqlHandler 注册过的函数 |
Disallow |
不允许任何函数表达式 |
FunctionExprValidator 提供了三个预配置的静态实例:
// 允许所有函数
var validatorAllowAll = FunctionExprValidator.AllowAll;
// 仅允许注册的函数
var validatorAllowRegisted = FunctionExprValidator.AllowRegisted;
// 不允许任何函数
var validatorDisallow = FunctionExprValidator.Disallow;
var validator = new FunctionExprValidator(FunctionPolicy.AllowRegisted);
| 环境或角色 | 推荐策略 |
|---|---|
| 本地开发 / 内部工具 | AllowAll |
| 生产环境普通业务查询 | AllowRegisted |
| 完全禁止自定义函数的受限场景 | Disallow |
FunctionExprValidator 继承自 ExprValidator,单个节点校验走 Validate(node),整棵表达式树校验更常见的用法是 VisitAll(expr):
using static LiteOrm.Common.Expr;
public override bool Validate(Expr node)
{
if (node == null) return true;
if (node is FunctionExpr funcExpr)
{
switch (FunctionPolicy)
{
case FunctionPolicy.AllowAll:
return true;
case FunctionPolicy.AllowRegisted:
return SqlBuilder.Instance.TryGetFunctionSqlHandler<SqlBuilder>(
funcExpr.FunctionName, out _);
case FunctionPolicy.Disallow:
return false;
}
}
return true;
}
在多租户场景下,限制不同租户可以使用函数:
public class UserQueryService
{
private readonly FunctionExprValidator _validator;
public UserQueryService(UserRole role)
{
_validator = role == UserRole.Admin
? FunctionExprValidator.AllowAll
: FunctionExprValidator.AllowRegisted;
}
public async Task<List<User>> SearchAsync(Expr query)
{
// 验证查询表达式
if (!_validator.VisitAll(query))
throw new InvalidOperationException("查询包含不允许的函数表达式");
return await _userViewDAO.Search(query).ToListAsync();
}
}
public class SafeUserDAO : ObjectViewDAO<User>
{
private static readonly FunctionExprValidator Validator =
FunctionExprValidator.AllowRegisted;
public async Task<List<User>> SafeSearchAsync(Expr expr)
{
if (!Validator.VisitAll(expr))
throw new SecurityException("表达式验证失败");
return await Search(expr).ToListAsync();
}
}
在查询执行前进行全局验证:
public class QueryInterceptor
{
private readonly FunctionExprValidator _validator;
public QueryInterceptor(FunctionPolicy policy)
{
_validator = new FunctionExprValidator(policy);
}
public void Intercept(Expr query)
{
if (!_validator.VisitAll(query))
{
throw new UnauthorizedAccessException(
"查询包含未授权的函数表达式");
}
}
}
一个典型流程是:先注册允许使用的扩展函数,再在执行查询前用验证器做兜底校验。
MySqlBuilder.Instance.RegisterFunctionSqlHandler("DATE_FORMAT", ...);
var validator = FunctionExprValidator.AllowRegisted;
var expr = new FunctionExpr("DATE_FORMAT", ...);
if (!validator.VisitAll(expr))
throw new InvalidOperationException("函数未注册,禁止执行");
FunctionExprValidator.AllowRegisted 依赖于 SqlBuilder.RegisterFunctionSqlHandler 注册的函数:
// 注册函数处理器
MySqlBuilder.Instance.RegisterFunctionSqlHandler("DATE_FORMAT", ...);
MySqlBuilder.Instance.RegisterFunctionSqlHandler("CONCAT", ...);
// AllowRegisted 验证器只允许这些注册的函数
var validator = FunctionExprValidator.AllowRegisted;
// DATE_FORMAT - 允许(已注册)
var expr1 = new FunctionExpr("DATE_FORMAT", ...);
validator.VisitAll(expr1); // true
// CUSTOM_UNREGISTERED - 拒绝(未注册)
var expr2 = new FunctionExpr("CUSTOM_UNREGISTERED", ...);
validator.VisitAll(expr2); // false
validator.VisitAll(expr);Validate(node) 更适合实现验证器本身时覆盖。AllowRegisted 策略。