共计 3662 个字符,预计需要花费 10 分钟才能阅读完成。
需要起因
在 Web 开发中,通常须要对前端(front-end)传到后端(back-end)的数据进行过滤和校验,以防备诸如 SQL 注入、XSS 注入之类的攻打。
在 Java 中,通过继承 HttpServletRequestWrapper 类可从新包装以后申请(Http Request)并将其替换,它的实现如下:
public class RequestWrapper extends HttpServletRequestWrapper {
private String AMP = "&";
private String queryString;
public RequestWrapper(HttpServletRequest request, String queryString)
{super(request);
this.queryString = queryString;
}
public String getQueryString()
{
String query = null;
if (super.getQueryString() != null) {query = super.getQueryString() + AMP + this.queryString;
} else {query = this.queryString;}
return query;
}
}
public class RequestHandler{
public void changeRequest{RequestWrapper reqWrapper = new RequestWrapper(httpRequest, newQueryString() );
FilterChain.doFilter(reqWrapper, servletResponse); // this FilterChain guy helps to replace the old request to
// new request to runtime
}
}
然而,Asp.NET 中并没有相似的包装器实现,通常的做法是在每一个申请通过(ProcessRequest)后再进行合法性校验,它导致的问题是每个申请 Action 都须要反复调用过滤办法来过滤申请参数。这显然冗余且麻烦得不太正当。
Asp.NET 中的一种可行实现思路
在 IIS 7.0+ 中,能够通过 HttpModule
来实现。
前置常识
上面列举了一些可能须要提前理解的知识点
- HttpRequest.Filter 微软给的简略示例
- IIS 7.0 的 ASP.NET 应用程序生命周期概述
为了无效地设置申请过滤器并利用它,应该在BeginRequest
事件中进行设置,这是申请验证和 url 映射之后的第一个事件。
步骤
上面以在 .Net MVC 中配置示例
1. 配置
<?xml version="1.0" encoding="utf-8"?>
<configuration>
// ignore other configuration
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<add name="httpModule" type="HttpRequestFilterModule" />
</modules>
</system.webServer>
</configuration>
2. 实现 HttpRequestFilterModule
using MvcApplication1.Filter;
using System.Web;
public class HttpRequestFilterModule : IHttpModule
{public void Dispose()
{throw new NotImplementedException();
}
public void Init(HttpApplication context)
{context.BeginRequest += new EventHandler(Application_BeginRequest);
}
public void Application_BeginRequest(object sender, EventArgs args)
{
HttpContext context = HttpContext.Current;
HttpRequest request = context.Request;
// 仅过滤 POST 申请
if (context.Request.HttpMethod != "POST")
{return;}
var requestCallback = new Func<string, string>(content => {
// may modify request content here
return "The request content is modified.";
});
context.Request.Filter = new RequestFilter(context.Request.Filter, context.Request.ContentEncoding, requestCallback);
}
}
RequestFilter.cs
using System;
using System.IO;
using System.Text;
namespace MvcApplication1.Filter
{
public class RequestFilter : Stream
{
// 长期缓冲区用于优化加载
private MemoryStream ms;
// 解决原始输入流
private Stream _stream;
// 响应的编码方式
private Encoding _encoding;
// 回调 delegate
private Func<string, string> _callback;
public RequestFilter(Stream stream, Encoding encoding, Func<string, string> callback)
{
_stream = stream;
_encoding = encoding;
_callback = callback;
}
public override void Flush()
{ms.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{return ms.Seek(offset, origin);
}
public override void SetLength(long value)
{ms.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count)
{if (ms == null)
{var sr = new StreamReader(_stream, _encoding);
string content = sr.ReadToEnd();
// 回调执行
content = _callback(content);
byte[] bytes = _encoding.GetBytes(content);
ms = new MemoryStream();
ms.Write(bytes, 0, bytes.Length);
ms.Seek(0, SeekOrigin.Begin);
}
return ms.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{throw new NotSupportedException();
}
public override bool CanRead
{get { return true;}
}
public override bool CanSeek
{get { return false;}
}
public override bool CanWrite
{get { return false;}
}
public override long Length
{get { return ms.Length;}
}
public override long Position
{get { return ms.Position;}
set {throw new NotSupportedException(); }
}
}
}
3. 验证后果
如果失常工作,拜访上面 Get Action, 将失去
The request content is modified.
的输入后果。
public class HomeController : Controller
{[HttpPost]
public ActionResult Get()
{
string requestBody = string.Empty;
using (StreamReader reader = new StreamReader(Request.InputStream))
{requestBody = reader.ReadToEnd();
}
return Content(requestBody);
}
}
正文完