从系列之中的上一篇文章,我介绍了如何支持Windows Live ID来登录自己的网站,这一篇按照顺序,我介绍如何支持使用Yahoo BBAuth来登录网站,和上面的一篇文章一样,我将会仅仅对具体的逻辑进行实现,不再重复基础的原理。
关于Yahoo BBAuth的更多信息,请参考:Browser-Based Authentication。
可以先看Yahoo提供的BBAuth的原理示意图:
第一步当然也是到Yahoo! Developer Network Home去注册你的程序,注册界面大致是这样的:
在注册完成之后,就可以在My Projects之中看到如下的应用程序信息:
当然,Shared Secret需要点击链接才能看到,这里不得不要批评一下BBAuth的是,所有的信息注册之后没有看到什么地方可以修改,要改动一个什么(例如底下的访问数据列表),都需要重新注册一个应用程序,这样的设计似乎有点奇怪,还有一点,就是Yahoo提供的文档虽然不怎么样(我曾经因为一个时间格式问题被折磨了好久),各个范例和源码其实是很多的,只是不太好找,可能是我的英文功底不够吧。
好,闲话少说,在经过注册之后,得到了这样几个参数:appid,secret,这几个参数在程序之中需要使用的。
先看如何得到用来让用户登录的转向地址,这个非常容易,那几个参数也是现成的,只是格式是严格要求的,一定不能出错,否则反正Yahoo的BBAuth服务器反正就是给你出个错,让你摸不着头脑了,详细的代码还是在下面看看代码吧。
得到转向地址并转向给Yahoo之后,就可以将用户转向到Yahoo的公共登录界面,用户登录之后,就会出现关于登录许可的提示,用户只有点击同意才能继续登录的过程。
登录完成之后,就开始Yahoo的服务器的就开始回转到你的Web应用程序,回传之后,根据请求的参数,需要按顺序完成以下步骤:
1.检查回传得Token是否正常;
2.根据回传的Token向Yahoo的服务器请求WSSID;
3.根据WSSID向Ymail接口请求用户的E-mail地址;
得到E-mail地址之后,就完成了整个登录过程。
下面是实现过程的代码:
1 public class BBAuthServer:BaseServer
2 {
3 private string appid, secret, server, pathLogin, pathPwtoken_login;
4 private int timeout=300;
5 //采用Web.Config之中的XML节点作为构造函数参数
6 public BBAuthServer(System.Xml.XmlNode node)
7 : base(node)
8 {
9 for (int i = 0; i < node.Attributes.Count; i++)
10 {
11 switch (node.Attributes[i].LocalName)
12 {
13 case "appid":
14 appid = node.Attributes[i].Value;
15 break;
16 case "secret":
17 secret = node.Attributes[i].Value;
18 break;
19 case "server":
20 server = node.Attributes[i].Value;
21 break;
22 case "pathLogin":
23 pathLogin = node.Attributes[i].Value;
24 break;
25 case "pathPwtoken_login":
26 pathPwtoken_login = node.Attributes[i].Value;
27 break;
28 case "timeout":
29 timeout = int.Parse(node.Attributes[i].Value);
30 break;
31 }
32 }
33 }
34 public bool checkRequest(HttpRequest Request)
35 {
36 string ts = Request["ts"];
37 string sig = Request["sig"];
38 //先检查时间
39 if (Math.Abs(long.Parse(ts) - ((long)((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds))) > timeout)
40 {
41 // throw new Exception("Error parsing timeout.");
42 }
43 //再检查签名
44 string baseString = System.Text.RegularExpressions.Regex.Replace(Request.Url.PathAndQuery, "&sig=[^&]+", "");
45 if (System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(baseString + secret, "MD5").ToLower() != sig)
46 {
47 throw new Exception("Signature mismatch:" + baseString);
48 }
49 return true;
50 }
51 public string getWSSID(string token,out string cookie)
52 {
53 string ts = ((long)((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds)).ToString();
54 string baseString = pathPwtoken_login+"appid=" + HttpUtility.UrlEncode(appid) + "&token=" + HttpUtility.UrlEncode(token) + "&ts=" + ts + "";
55 string sig = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(baseString + secret, "MD5").ToLower();
56 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(server + baseString + "&sig=" + sig));
57 request.Method = "GET";
58 HttpWebResponse response = (HttpWebResponse)request.GetResponse();
59 XmlDocument doc = new XmlDocument();
60 doc.Load(response.GetResponseStream());
61 cookie = doc.SelectSingleNode("//*[local-name()='Cookie']").InnerText.Trim().Substring(2);
62 return doc.SelectSingleNode("//*[local-name()='WSSID']").InnerText.Trim();
63 }
64 public string getUserName(string token,string wssid,string cookie)
65 {
66 try
67 {
68 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri("http://mail.yahooapis.com/ws/mail/v1.1/soap?appid=" + HttpUtility.UrlEncode(appid) + "&WSSID=" + HttpUtility.UrlEncode(wssid)));
69 request.Method = "POST";
70 request.CookieContainer = new System.Net.CookieContainer();
71 CookieCollection collection = new CookieCollection();
72 request.CookieContainer.Add(new Uri("http://mail.yahooapis.com/"), new Cookie("Y", cookie));
73 byte[] bytes = Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"utf-8\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><SOAP-ENV:Body><GetUserData xmlns=\"urn:yahoo:ymws\"></GetUserData></SOAP-ENV:Body></SOAP-ENV:Envelope>");
74 request.Headers.Add("SOAPAction", "\"\"");
75 request.ContentType = "application/soap+xml; charset=utf-8";
76 request.ContentLength = bytes.Length;
77 Stream rs = request.GetRequestStream();
78 rs.Write(bytes, 0, bytes.Length);
79 rs.Close();
80 HttpWebResponse response = getResponse(request);
81 XmlDocument doc = new XmlDocument();
82 doc.Load(response.GetResponseStream());
83 return doc.SelectSingleNode("//*[local-name()='defaultID']").InnerText;
84 /**//*
85 ymws ymwsInstance = new ymws();
86 ymwsInstance.Url = "http://mail.yahooapis.com/ws/mail/v1.1/soap?appid=" + appid + "&wssid=" + wssid;
87 ymwsInstance.CookieContainer = new System.Net.CookieContainer();
88 CookieCollection collection = new CookieCollection();
89 ymwsInstance.CookieContainer.Add(new Uri("http://mail.yahooapis.com/"), new Cookie("Y", cookie));
90 GetUserDataResponse userData = ymwsInstance.GetUserData(new GetUserData());
91 return userData.data.userSendPref.defaultID;*/
92 }
93 catch (Exception)
94 {
95 return wssid;
96 }
97 }
98 public override string getLoginUrl()//生成登录的URL
99 {
100 string ts = ((long)((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds)).ToString();
101 string baseString = pathLogin+"appid=" + HttpUtility.UrlEncode(appid) + "&appdata=" + HttpUtility.UrlEncode(name) + "&send_userhash=1&ts=" + ts + "";
102 string sig = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(baseString + secret, "MD5").ToLower();
103 return server + baseString + "&sig=" + sig;
104 }
105 public override void parseHandle(HttpContext page)//处理回转请求
106 {
107 checkRequest(page.Request);//检查回传请求是不是合法
108 string cookie;
109 string wssid = getWSSID(page.Request["token"],out cookie);//先获取wssid
110 string name = getUserName(page.Request["token"], wssid, cookie);//通过wssid获取用户名
111 //检查完毕,开始获得用户的WSSID
112 AccountHelper.setUserInfo(page.Request["userhash"], name, this.name);
113 AccountHelper.returnOpener();
114 page.Response.End();
115 }
116 public static HttpWebResponse getResponse(HttpWebRequest request)
117 {
118 try
119 {
120 return (HttpWebResponse)request.GetResponse();
121 }
122 catch (WebException e)
123 {
124 HttpContext.Current.Response.Write(e.Message);
125 string result = new StreamReader(e.Response.GetResponseStream()).ReadToEnd();
126 HttpContext.Current.Response.Write(result);
127 HttpContext.Current.Response.End();
128 }
129 return null;
130 }
131 }