这其实是以前工作中的一个功能,当时觉得很好奇,是如何实现自动加密页面中的url的,如GridView中的链接绑定等。通过加密后的url的表现形式就如page.aspx?EncodingQuery=2IYZ2UW1bS2rPAuKZf4WWw==。今天在复习asp.net的基础常识时,既然想起,就来看看是如何实现的(这里主要还是参考DC.Web.HttpCompress)。
1、首先,新建一个asp.net网站项目,在默认的页面上我们来绑定一个Gridview控件。
View Code
<div> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" Height="285px" Width="638px"> <Columns> <asp:HyperLinkField DataNavigateUrlFields="Id" DataNavigateUrlFormatString="~/page.aspx?id={0}" HeaderText="Title" DataTextField="Name" /> </Columns> </asp:GridView> </div>
2、 在页面的后台方法中进行绑定数据:
View Code
1 List<LinkModel> list = new List<LinkModel>(2); 2 list.Add(new LinkModel() 3 { 4 Id ="11", 5 Name = "ltq" 6 }); 7 list.Add(new LinkModel() 8 { 9 Id = "12" 10 ,Name = "hb" 11 }); 12 GridView1.DataSource = list; 13 GridView1.DataBind();
3、 点击F5,然后查看生成的源代码:
可以看到,这里还是原始的未加密的url字符串。
4、 好,现在新增一个HttpModel.cs:
View Code
1 public class HttpModule : IHttpModule 2 { 3 public void Dispose() 4 { 5 6 } 7 8 public void Init(HttpApplication context) 9 { 10 11 context.BeginRequest += context_BeginRequest; 12 13 context.PostReleaseRequestState += context_PostReleaseRequestState; 14 } 15 16 17 void context_BeginRequest(object sender, EventArgs e) 18 { 19 HttpApplication app = (HttpApplication) sender; 20 HttpContext context = app.Context; 21 string currentPath = context.Request.Url.PathAndQuery; 22 int index = currentPath.IndexOf("?EncodingQuery="); 23 if(index>0) 24 { 25 string eQuery = currentPath.Substring(index+1); 26 string encryptQuery = currentPath.Substring(index + 15); 27 string query = AESUtil.Decrypt(encryptQuery,CommonUtil.AesKey ); 28 currentPath = currentPath.Replace(eQuery, query); 29 context .RewritePath(currentPath); 30 } 31 } 32 33 private void context_PostReleaseRequestState(object sender, EventArgs e) 34 { 35 HttpApplication app = (HttpApplication) sender; 36 37 if (app.Request["HTTP_X_MICROSOFTAJAX"] != null) 38 return; 39 40 // fix to handle caching appropriately 41 // see http://www.pocketsoap.com/weblog/2003/07/1330.html 42 // Note, this header is added only when the request 43 // has the possibility of being compressed... 44 // i.e. it is not added when the request is excluded from 45 // compression by CompressionLevel, Path, or MimeType 46 string realPath = ""; 47 Configuration settings = null; 48 // get the config settings 49 50 app.Context.Response.Cache.VaryByHeaders["Accept-Encoding"] = true; 51 string acceptedTypes = app.Request.Headers["Accept-Encoding"]; 52 53 // if we couldn't find the header, bail out 54 if (acceptedTypes == null) 55 return; 56 57 // Current response stream 58 59 //Stream baseStream = app.Response.Filter; 60 CompressionPageFilter filter = new CompressionPageFilter(app.Response.Filter); 61 filter .AesKey =CommonUtil .AesKey; 62 63 filter.App = app; 64 app.Response.Filter = filter; 65 66 // check for buggy versions of Internet Explorer 67 if (app.Context.Request.Browser.Browser == "IE") 68 { 69 if (app.Context.Request.Browser.MajorVersion < 6) 70 return; 71 else if (app.Context.Request.Browser.MajorVersion == 6 && 72 !string.IsNullOrEmpty(app.Context.Request.ServerVariables["HTTP_USER_AGENT"]) && 73 app.Context.Request.ServerVariables["HTTP_USER_AGENT"].Contains("EV1")) 74 return; 75 } 76 acceptedTypes = acceptedTypes.ToLower(); 77 78 if (filter.Compress != "none") 79 app.Response.AppendHeader("Content-Encoding", filter.Compress); 80 } 81 82 private class CompressionPageFilter : Stream 83 { 84 85 public string AesKey { get; set; } 86 private HttpApplication app; 87 88 public HttpApplication App 89 { 90 get { return app; } 91 set { app = value; } 92 } 93 94 private string compress = "none"; 95 96 public string Compress 97 { 98 get { return compress; } 99 set { compress = value; } 100 } 101 102 private StringBuilder responseHtml; 103 104 105 106 private const string _linkPattern = 107 "(?<HTML><a[^>]*href\\s*=\\s*[\\\"\\']?(?<HREF>[^\"'>\\s]*)[\\\"\\']?[^>]*>)"; 108 109 public CompressionPageFilter(Stream sink) 110 { 111 _sink = sink; 112 responseHtml = new StringBuilder(); 113 } 114 115 private Stream _sink; 116 117 #region Properites 118 119 public override bool CanRead 120 { 121 get { return true; } 122 } 123 124 public override bool CanSeek 125 { 126 get { return true; } 127 } 128 129 public override bool CanWrite 130 { 131 get { return true; } 132 } 133 134 public override void Flush() 135 { 136 _sink.Flush(); 137 } 138 139 public override long Length 140 { 141 get { return 0; } 142 } 143 144 private long _position; 145 146 public override long Position 147 { 148 get { return _position; } 149 set { _position = value; } 150 } 151 152 #endregion 153 154 #region Methods 155 156 public override int Read(byte[] buffer, int offset, int count) 157 { 158 return _sink.Read(buffer, offset, count); 159 } 160 161 public override long Seek(long offset, SeekOrigin origin) 162 { 163 return _sink.Seek(offset, origin); 164 } 165 166 public override void SetLength(long value) 167 { 168 _sink.SetLength(value); 169 } 170 171 public override void Close() 172 { 173 _sink.Close(); 174 } 175 176 public override void Write(byte[] buffer, int offset, int count) 177 { 178 string strBuffer = UTF8Encoding.UTF8.GetString(buffer, offset, count); 179 180 // --------------------------------- 181 // Wait for the closing </html> tag 182 // --------------------------------- 183 Regex eof = new Regex("</html>", RegexOptions.IgnoreCase); 184 185 responseHtml.Append(strBuffer); 186 187 if (eof.IsMatch(strBuffer)) 188 { 189 // when compressing the html, some end characters are cut off. Add some spaces so it cuts the spaces off instead of important characters 190 responseHtml.Append(Environment.NewLine + Environment.NewLine + Environment.NewLine + 191 Environment.NewLine + Environment.NewLine + Environment.NewLine + 192 Environment.NewLine + Environment.NewLine); 193 string html = responseHtml.ToString(); 194 195 // replace the css and js with HttpHandlers that compress the output 196 html = ReplaceLink(html); 197 198 199 byte[] data = UTF8Encoding.UTF8.GetBytes(html); 200 201 202 _sink.Write(data, 0, data.Length); 203 } 204 } 205 206 public string ReplaceLink(string html) 207 { 208 // create a list of the stylesheets 209 List<string> stylesheets = new List<string>(); 210 // create a dictionary used for combining css in the same directory 211 Dictionary<string, List<string>> css = new Dictionary<string, List<string>>(); 212 213 // create a base uri which will be used to get the uris to the css 214 Uri baseUri = new Uri(app.Request.Url.AbsoluteUri); 215 216 // loop through each match 217 foreach (Match match in Regex.Matches(html, _linkPattern, RegexOptions.IgnoreCase)) 218 { 219 // this is the enire match and will be used to replace the link 220 string linkHtml = match.Groups[0].Value; 221 // this is the href of the link 222 string href = match.Groups[2].Value; 223 224 // get a uri from the base uri, this will resolve any relative and absolute links 225 Uri uri = new Uri(baseUri, href); 226 string file = ""; 227 // check to see if it is a link to a local file 228 if (uri.Host == baseUri.Host) 229 { 230 // check to see if it is local to the application 231 if (uri.AbsolutePath.ToLower().StartsWith(app.Context.Request.ApplicationPath.ToLower())) 232 { 233 // this combines css files in the same directory into one file (actual combining done in HttpHandler) 234 int index = uri.AbsolutePath.LastIndexOf("/"); 235 string path = uri.AbsolutePath.Substring(0, index + 1); 236 file = uri.AbsolutePath.Substring(index + 1); 237 if (!css.ContainsKey(path)) 238 css.Add(path, new List<string>()); 239 css[path].Add(file + (href.Contains("?") ? href.Substring(href.IndexOf("?")) : "")); 240 // replace the origianl links with blanks 241 var query = uri.Query; 242 if (query != "") 243 { 244 string temp = query.Substring(1); 245 string encryQuery = "?EncodingQuery=" + AESUtil.Encrypt(temp, AesKey); 246 string newlinkHtml = linkHtml.Replace(query, encryQuery); 247 html = html.Replace(linkHtml, newlinkHtml); 248 249 } 250 251 continue; 252 } 253 254 } 255 256 } 257 258 return html; 259 } 260 261 262 263 264 265 #endregion 266 267 } 268 }
代码较长,但应该很清晰,在代码中是通过AES加解密链接字符串的参数的。
5、注册这个HttpModel,在web.config中添加即可:
1 <system.webServer> 2 <handlers> 3 </handlers> 4 <modules> 5 <add name="HttpCompressModule" type="HttpModelTest.HttpModule"/> 6 </modules> 7 </system.webServer>
因为是在Vs2012下进行的测试,配置文件与在iis6中的配置是不同的。
6、再次F5进入调试,查看源代码:
可以看到,这里的参数都已经被加密了。
7、 我们再来测试看看能不能正确解密这个字符串,在page.aspx的后台代码中添加如下代码:
1 protected void Page_Load(object sender, EventArgs e) 2 { 3 string id = Request["id"]; 4 Response.Write(id); 5 }
F5进入测试:
可以看到,正确输出id值。
8、总结:
a)加解密算法的选择是否正确,这个还得研究,毕竟感觉加密后的url的字符太多了。
b)正则获取url的是否正确,会不会把其他文本内容中的url也加密了?
c)性能,每次这样的遍历页面,进行匹配,对性能的影响有多大?