博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
我的Ajax服务端框架 - 安全问题,初始化设置,实现原理
阅读量:7109 次
发布时间:2019-06-28

本文共 9011 字,大约阅读时间需要 30 分钟。

我的Ajax服务端框架 - 安全问题

通过前面章节的示例代码,您会发现一个问题:那就是在JS中可以调用所有的C#的方法(理论上是可以调用任何一个程序集中的所有Public类的所有方法)。如果您认为这样做,有安全问题,那么可以订阅事件 OnAjaxCall 来过滤请求。FishWebLib提供的Handler或者Module都有这个事件,您可以统一处理。可参考以下代码:

// Ajax调用的安全检查事件。FishWebLib.Ajax.AjaxMethodV2Handler.OnAjaxCall += new FishWebLib.Ajax.AjaxCallCheckHandler(AjaxMethodV2Handler_OnAjaxCall);
/// /// Ajax调用检查/// /// static void AjaxMethodV2Handler_OnAjaxCall(FishWebLib.Ajax.AjaxCallEventArgs e){    // ##################################################################################################    // 在这里可以做一些在Ajax调用时的安全检查。    // ##################################################################################################        // 如果经过您的检查逻辑,不允许一个调用请求,可以做如下处理:    //e.IsAllowed = false;    //e.DenyMessage = "请求的资源不允许访问。";    // 在本示例中,就不处理了。因为在另一个地方,我仍然有机会处理。}

AjaxCallEventArgs的定义请见后文。

AjaxMethodV1Handler的安全检查示例

// Ajax调用的安全检查事件。FishWebLib.Ajax.AjaxMethodV1Handler.OnAjaxCall += new FishWebLib.Ajax.AjaxCallCheckHandler(AjaxMethodV1Handler_OnAjaxCall);
static void AjaxMethodV1Handler_OnAjaxCall(FishWebLib.Ajax.AjaxCallEventArgs e){    string fileName = System.IO.Path.GetFileNameWithoutExtension(e.context.Request.PhysicalPath);    // 在这里,我将只检查要调用的类名是不是以Ajax开头,如果不是,则不允许调用。    if( fileName.StartsWith("Ajax", StringComparison.OrdinalIgnoreCase) == false ) {        e.IsAllowed = false;        e.DenyMessage = "不允许调用指定的类方法。";    }    // 要调用的方法名中URL的查询字符串中,也可以检查到。这里就不检查了。}

UserControlHandler 和 PageMethodModule 也有 OnAjaxCall 事件,可以按上面的方式来类似处理。

您也可以定义一个统一的安全检查方法,只要符合下面的委托定义即可:

namespace FishWebLib.Ajax{    ///     /// AJAX调用发生时的委托类型    ///     /// AjaxCallEventArgs类型的事件参数    public delegate void AjaxCallCheckHandler(AjaxCallEventArgs e);    ///     /// 发生AJAX调用时的事件参数    ///     public sealed class AjaxCallEventArgs : System.EventArgs    {        ///         /// 本次请求的HttpContext实例        ///         public HttpContext context;        ///         /// 调用类型        ///         public AjaxCallType AjaxCallType;        ///         /// 调用是否允许        ///         public bool IsAllowed = true;        ///         /// 当设置IsAllowed=false时,可为本成员设置一个用于表示禁止访问的消息。        ///         public string DenyMessage;        ///         /// 构造方法        ///         /// HttpContext对象        /// Ajax调用类型        public AjaxCallEventArgs(HttpContext cxt, AjaxCallType type)        {            this.context = cxt;            this.AjaxCallType = type;        }    }    ///     /// AJAX调用类型    ///     public enum AjaxCallType    {        ///         /// 调用C#方法,由AjaxMethodV1Handler引发        ///         AjaxMethodV1,        ///         /// 调用用户控件,由UserControlHandler引发        ///         UserControl,        ///         /// 调用页面方法,由PageMethodModule引发        ///         PageMethod,        ///         /// 调用C#方法,由AjaxMethodV2Handler引发        ///         AjaxMethodV2    }}

如果上面的处理方式仍不能满足要求,那么请创建自己的ashx处理器,实现您自定义的过滤检查,然后调用FishWebLib.Ajax.MethodExecutor中的以下方法:

public static void ProcessRequest(HttpContext context, Type type, string method)

我的Ajax服务端框架 - 初始化设置

请参考以下代码:(在演示程序的AppHelper.cs中可以找到)

AjaxMethodV2Handler的初始化设置

/// /// 设置AjaxMethodV2Handler查找类型时的工作方式。/// static void SetAjaxClassSearchMode(){    // 这里先说明一下:    // 当AjaxMethodV2Handler被Asp.net调用时,需要知道要调用哪个类型的哪个方法。    // 在AjaxMethodV2Handler的默认实现中,调用了AjaxClassSearchHelper.Parse,    //    这个方法分析URL,并根据指定的类型查找模式,去查找指定的类型,并获取一个方法名称。    // ##################################################################################################    // 这里,我们有二种选择:    // ##################################################################################################    // 1. 自己实现一个 ParseTypeMethodPairFromRequest 的委托并赋值给AjaxMethodV2Handler.ParseFunc,这样做有二个好处:    //     a. 可以实现自己认为更方便的URL,比如URL:/Classname/MethodName.ext    //     b. 可以检查指定的类型是否允许被Ajax调用。(###安全检查###)    // 2. 保持默认的设置,但需要简单的2个配置AjaxClassSearchHelper。    //FishWebLib.Ajax.AjaxClassSearchHelper.Placeholder = ;    // 这个参数这里就不设置了,保持默认值。    FishWebLib.Ajax.AjaxClassSearchHelper.ClassNameSearchPattern = new string[] {         // 建议将全部供Ajax调用的类,放在一个命名空间下,这样也可以保证在客户端的JS不至于可以调用所有的类型        // 还可以像Asp.net MVC那样,为所有允许Ajax调用的类,取一个后缀,或者前缀也是可行的。        typeof(MyLab.AjaxService.AjaxOrder).AssemblyQualifiedName.Replace("AjaxOrder", "{0}")    };    // #############################################################################################3    // 在本网站中,我们选择第一种方法,但为了方便,我将仍借助于第二种方法来简化实现。    FishWebLib.Ajax.AjaxMethodV2Handler.ParseFunc = MyParseTypeMethodPairFromRequest;}/// /// 根据当前请求获取要调用的类型及方法名/// /// /// 
static FishWebLib.Ajax.TypeMethodPair MyParseTypeMethodPairFromRequest(HttpContext context){ // 使用FishWebLib提供的方法,简化实现。 FishWebLib.Ajax.TypeMethodPair result = FishWebLib.Ajax.AjaxClassSearchHelper.Parse(context); if( result != null ) { // 在这里,我还可以检查将要调用的类型和方法是否是允许的。 // 这里的规则很简单:如果不是Ajax开头的类型,将不允许访问 if( result.Type.Name.StartsWith("Ajax") == false ) { // 转向另一个方法的调用,或者返回 null 也是表示禁止访问。 result = new FishWebLib.Ajax.TypeMethodPair(typeof(AppHelper), "DenyAjaxAccess"); } } return result;}/// /// 禁止Ajax访问时要调用的方法/// ///
public static string DenyAjaxAccess(){ return "请求的资源不允许访问。";}

AjaxMethodV1Handler的初始化设置

// 注意:下面的配置指定AjaxMethodV1Handler查找类型的程序集范围。FishWebLib.Ajax.AjaxMethodV1Handler.AjaxAssemblyName = typeof(AjaxTestClass).Assembly.ToString();

统一的异常处理

// 设置Ajax 调用时的异常事件,处理异常。FishWebLib.Ajax.AjaxExceptionHelper.OnAjaxInvokeException += 			new FishWebLib.Ajax.AjaxExceptionAction(AjaxExceptionHelper_OnAjaxInvokeException);
/// /// Ajax异常处理/// /// static void AjaxExceptionHelper_OnAjaxInvokeException(FishWebLib.Ajax.AjaxExceptionEventArgs e){    // 指示异常已经过处理    e.ExceptionHandled = true;    // 异常的处理方式也很简单:把异常写入到响应流,并保存异常。    e.context.Response.Write(e.Exception.GetBaseException().Message);    SafeLogException(e.Exception);}

我的Ajax服务端框架 - 实现原理

本文将分别介绍FishWebLib提供的三个Handler及一个Module的实现原理。

1. AjaxMethodV1Handler

AjaxMethodV1Handler的主要实现代码如下:

public void ProcessRequest(HttpContext context){    if( string.IsNullOrEmpty(AjaxAssemblyName) ) {        AjaxCallChecker.WriteSimpleMessage(context, SR.AjaxAssemblyNameIsNull);        return;    }    if( AjaxCallChecker.RaiseCheckEvent(context, OnAjaxCall, AjaxCallType.AjaxMethodV1) == false )         return;        string className = System.IO.Path.GetFileNameWithoutExtension(context.Request.PhysicalPath);    Type type = TypeManager.GetTypeByName(string.Concat(className, ", ", AjaxAssemblyName));    if( type == null ) {        AjaxExceptionHelper.ProcessException(context, new Exception(string.Format(SR.TypeNotFound, className)));        return;    }    MethodExecutor.ProcessRequest(context, type);}

从以上代码可以看出,处理器非常简单:根据要请求的文件名,去掉扩展名,当成类名,然后与参数AjaxAssemblyName合并,得到一个类名的完全限定形式, 最后获取要调用类的具体类型,然后把请求交给MethodExecutor.ProcessRequest()来处理,在那里将会从URL的查询字符串中读取参数method, 就可以得到要调用的方法名。有了类型与方法名后,就可以唯一确定一个方法了,最后只需要去调用就可以了。

至于如何调用方法,如何给方法的参数赋值,最后如何处理返回值给客户端,就属于框架本身的事情了。

所有的这一切,对于客户端来说,更是透明的。这些透明的实现也就是框架的意义了。

2. AjaxMethodV2Handler

AjaxMethodV2Handler的主要实现代码如下:

private static ParseTypeMethodPairFromRequest s_ParseFunc = AjaxClassSearchHelper.Parse;public void ProcessRequest(HttpContext context){    if( AjaxCallChecker.RaiseCheckEvent(context, OnAjaxCall, AjaxCallType.AjaxMethodV2) == false )        return;    TypeMethodPair pair = s_ParseFunc(context);    if( pair == null ) {        AjaxExceptionHelper.ProcessException(context, new Exception(SR.InvalidRequest));        return;    }        MethodExecutor.ProcessRequest(context, pair.Type, pair.Method);}

如果比较AjaxMethodV1Handler的实现,可以发现,AjaxMethodV2Handler更简单。最终也是把请求交给MethodExecutor.ProcessRequest()来处理。 其实,这二个处理器与PageMethodModule的实现是比较类似的:获取一个类型和一个方法名,扔给MethodExecutor就完事了。

只是AjaxMethodV2Handler把“获取类型和方法名”的过程交给委托的实现来处理了。
默认的实现使用了:AjaxClassSearchHelper.Parse ,它能拆分这种形式的URL: class.method.xx
AjaxClassSearchHelper定义了二个数据成员:

/// /// 在URL中用于分隔类名和方法名的特殊字符,默认值:'.'/// public static char Placeholder = '.';/// /// 用于搜索类类型的搜索模式字符串数组。搜索模式通常是一个类型的完全限定字符串中将类名改成{0}/// public static string[] ClassNameSearchPattern = null;

可以这样设置ClassNameSearchPattern:

FishWebLib.Ajax.AjaxClassSearchHelper.ClassNameSearchPattern = new string[] {     typeof(MyLab.AjaxService.AjaxOrder).AssemblyQualifiedName.Replace("AjaxOrder", "{0}")};

3. UserControlHandler

UserControlHandler的主要实现代码如下:

public void ProcessRequest(HttpContext context){    if( AjaxCallChecker.RaiseCheckEvent(context, OnAjaxCall, AjaxCallType.UserControl) == false )        return;    string filePath = context.Request.AppRelativeCurrentExecutionFilePath;    // 这里不检查指定的用户控件是否存在,如果不存在Asp.net会告诉调用方的。        UcExecutor.ProcessRequest(context, filePath, true);}

从代码可以看出:请求最后是由UcExecutor来处理的。

所以,也可以不使用这个处理器,而是将请求交给C#方法来处理,获取数据后,再去调用UcExecutor.ProcessRequest(),这种做法是符合MVC的设计思想的。

4. PageMethodModule

PageMethodModule的实现与前二个处理器类似,不一样的地方在于它是以Module的形式存在的。

为了能够调用MethodExecutor.ProcessRequest(),它也需要知道一个类型和一个方法名。 有了请求页面地址,就可以知道当前在请求哪个页面,自然也就能获取一个类型了,方法名可以通过从FORM中获取, PageMethodModule会尝试读取FORM中键名为"AjaxPageMethod"对应的值,如果找到,后面的事情就如前面所说的那样处理了。
当然,为了性能,PageMethodModule只会处理POST请求。如果没有从FROM找到方法名,也会忽略本次请求。

转载地址:http://zvvhl.baihongyu.com/

你可能感兴趣的文章
mysql查询当天,前一天,一周,一个月
查看>>
Python学习一:序列基础详解
查看>>
数组排序
查看>>
Recycleview实现复杂布局
查看>>
jq禁止与启动滚动条
查看>>
ffmpeg去水印
查看>>
JSF通过超链接传递参数到控制层
查看>>
C#入门篇-3:数据类型及转换
查看>>
Leetcode Simplify Path
查看>>
Kafka吞吐量测试案例
查看>>
easyui datagrid 前后台代码
查看>>
洛谷P1416 进攻火星 数学
查看>>
学习Verilog的一个博客
查看>>
Python Selenium常见用法介绍
查看>>
Generating Complex Procedural Terrains Using GPU
查看>>
商品标签例子——CSS3 transform 属性
查看>>
Android Xposed框架中创建模块的指导手册
查看>>
hdu 1006 Tick and Tick
查看>>
requirejs+angularjs搭建时手动创建app顶级模块
查看>>
JS中的单引号和双引号
查看>>