.Net 关于摸拟登录 并在登录成功后 使用IE打开 - 一路曲折
开发工具:VS2008 .NET 2.0
项目类型:BS
项目需求:开发一个账号管理系统,包括用户注册,用户登录,账号管理,快速登录等功能。
简单的流程如下:用户在账号管理系统中注册一个自己的账号,登录账号管理系统,在账号管理功能中,录入相应系统的账号与密码(系统是客户公司内之前就已开发好的,有BS端的也有cs端的,这篇文章中只说BS系统),已录入账号与密码的系统可以快速登录(快速登录界面的感觉,就是用户点击一下相就系统的快速登录,就看见浏览器新开一个窗口进行登录,并跳转到主页面)。
尝试过的解决方案:
解决方案一:
使用HttpClient 进行摸拟登录,登录成功后,使用 InternetSetCookie 与IE同步cookies, 最后使用 Response.Redirect(“”);进行跳转。
//写入函数 [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool InternetSetCookie(string lpszUrlName, string lbszCookieName, string lpszCookieData);
方案的问题: HttpClient摸拟登录成功后,打开IE跳转到首页时,Cookies 不能同步,导致跳转失败(登录失败);
失败方案
解决方案二:
方案二与方案一有很大的相似之处,不同的地方在于 方案一 中使用的是 Response.Redirect(); 进行跳转而方案二中使用 System.Diagnostics.Process.Start();
在测试的过程中发现,使用 System.Diagnostics.Process.Start(); 能够登录到一些验证简单的系统中一般说来就是一些企业级的嘛,像 163 邮箱就不能登录,人人网我在测试过程中也没有成功。
方案二应该能满足我项目的需求,因为客户暂时没有给出具体的网站地址,所以不好说得。所以标记为失败方案
解决方案三:
调用本地的IE浏览器,并发送数据,想想这种方案简单也好用,但是我忘记我是使用的BS项目了,所以还是做了个测试,下面贴出测试代码:
var myIE = new InternetExplorer(); myIE.Visible = true; object headers = "Content-Type: application/x-www-form-urlencoded" + Convert.ToChar(10) + Convert.ToChar(13) + "Referer:http://www.hao123.com/"; object postData = Encoding.ASCII.GetBytes("url=http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=15&username=" + txtUserID.Value.Trim() + "&password=" + txtPwd.Value.Trim()); object nullArg = null; myIE.Navigate("http://reg.163.com/CheckUser.jsp", ref nullArg, ref nullArg, ref postData, ref headers);
如果项目是CS的,我觉得这种解决方案相当的不错,但是我客户需要的是BS,也就在此时,我犯了一下二,信心满满的将测试代码放在IIS上,让同事给测试一下,结果,他们的邮箱都在我的电脑上登录成功了,呵呵(虽然如此但是的确都是登录成功的了)
失败方案
解决方案四:
根据用户快速登录的网站,根据提交的siteid找到当前登录用户相应的用户名,密码,然后生成一个form 表单,再自动提交form表单,最后来一个指定的跳转
具体的代码如下:
StringBuilder _txt = new StringBuilder("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n" + "<head>\r\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n" + "<title>百姓网快速登录</title>\r\n" + "<style type=\"text/css\" >\r\n" + "body{ font-size:14px;}\r\n" + "</style>\r\n" + "<script src=\"/js/jquery.js\" type=\"text/javascript\"></script>\r\n" + "<script type=\"text/javascript\">\r\n" + "function quickLoginSubmit() {\r\n" + "if (status==0) {\r\n" + "window.location.href = exeUrl;\r\n" + "}else if(status==1){\r\n" + " if (manageUrl) {\r\n" + " if(window.opener){\r\n" + " window.opener.window.setTimeout(\"loginWindow.location.href='\" + manageUrl + \"';\", 3000);\r\n" + " }else{\r\n" + " window.setTimeout(\"window.location.href='\" + manageUrl + \"';\", 3000);\r\n" + " }\r\n" + "}\r\n" + " $(\"#loginForm\").submit();\r\n" + "}\r\n" + "}\r\n" + "</script>\r\n" + "<script type=\"text/javascript\"> \r\n" + " var manageUrl = \"http://www.baixing.com/wo/postings/#active\";\r\n" + " var exeUrl = \"http://www.baixing.com/auth/denglu/?src=headerHome\";\r\n" + " var status = 1;\r\n" + " setTimeout(\"quickLoginSubmit();\",2000);\r\n" + "</script>\r\n" + "</head>\r\n" + "<body>\r\n" + "<img src=\"images/load.gif\" align=\"absmiddle\" >正在登录,请稍等...\r\n" + "<form id=\"loginForm\" method=\"post\" name=\"loginForm\" action=\"http://www.baixing.com/auth/denglu/?src=headerHome\">\r\n" + " <input value=\"账号\" type=\"hidden\" name=\"identity\" /><input value=\"密码\" type=\"hidden\" name=\"password\" /><input value=\"login\" type=\"hidden\" name=\"action\" /><input value=\"http://hangzhou.baixing.com/\" type=\"hidden\" name=\"redirect\" /><input type=\"hidden\" name=\"refer\" />\r\n" + "</form>\r\n" + "</body>\r\n" + "</html>\r\n"); Response.Write(_txt);
在我的需求中,方案四是最合适的了。
成功方案 在此感谢 Launcher ,是 launcher 给我指引了正确的方向,呵呵
在测试中发现,js 中的 XMLHttpRequest ,是不能修改Referer的值,虽然他有一个设置头的方法 ,代码如下:
<script type ="text/javascript"> var xmlHttp = null; function createXMLHttpRequest() { if (xmlHttp == null) { if (window.XMLHttpRequest) { //Mozilla 浏览器 xmlHttp = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE浏览器 try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { } } } } } //发送数据 function Post() { if (xmlHttp == null) { createXMLHttpRequest(); } var data = "url=http%3A%2F%2Fentry.mail.163.com%2Fcoremail%2Ffcg%2Fntesdoor2%3Flightweight%3D1%26verifycookie%3D1%26language%3D-1%26style%3D15&username=challengesoflife&password=zhukun1989"; xmlHttp.open("POST", "http://www.abc.com", true); xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlHttp.setRequestHeader("Referer", "http://www.a.com"); xmlHttp.setRequestHeader("Origin", "http://www.b.com"); xmlHttp.send(data); } //状态改变事件 function onreadystatechange() { if (xmlHttp.readyState == 4) { if (xmlHttp.status == 200) { alert('!!!'); window.location.href = "http://www.abc.com"; } } } </script>
在此附加获取cookies的方法 ,是在网上找的,出处已忘记了
public List<Cookie> GetAllCookies(CookieContainer cc) { List<Cookie> lstCookies = new List<Cookie>(); Hashtable table = (Hashtable)cc.GetType().InvokeMember("m_domainTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, cc, new object[] { }); foreach (object pathList in table.Values) { SortedList lstCookieCol = (SortedList)pathList.GetType().InvokeMember("m_list", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, pathList, new object[] { }); foreach (CookieCollection colCookies in lstCookieCol.Values) foreach (Cookie c in colCookies) lstCookies.Add(c); } return lstCookies; }