需要起因
在 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); }}