上面的几篇文章之中,我按照顺序讲解了Google的AuthSub,Yahoo的BBAuth,Live的Account ID,OpenID的登录,而这正是我在规划这个Step1账户登录系统时候设计要支持的几个网站,现在已经全部支持了,不过可惜的是这些都是国外的网站,国内的很多用户没有这样的账户的,这样,这个系统的可用性就会大大降低了,可惜国内的大网站们现在似乎都没有要开放API的意思,这一点是比较郁闷的。
我仔细的想过如何才能支持国内的一些网站的登录,结果还是没有什么办法,不过我忽然有一天在上校内网的时候,忽然想到,可不可以使用这些SNS的网站的接口来完成这个功能呢?现在SNS网站开放接口确实是像一阵风一样,包括51.com也开放了API,不过51.com是我比较鄙视的网站,也就不提它了。
我开始仔细的考虑整个登录过程,按照以前的每一个接口的逻辑,首先要生成一个登录网址,这个网址怎么得来呢?可以用我们自己开发的SNS APP的页面地址,这个网址通常是固定的,完全可以使用这个网址来作为我们系统的登录转向地址。
我们将用户转向到这个地址之后,因为SNS网站的内容都是需要用户登录的,所以,用户会被再次转向到SNS的登录界面,这样,用户的登录就开始了。
在用户登录完成之后,就会被重新转向到APP的页面,这样的话,APP就会被加载。
APP加载之后,其任务就是获取到用户的资料(ID和名称),然后带上这些参数,将整个页面转向回到登录系统,这样的话,整个登录过程就完成了。
上面所说的是一个基本原理,如果要确实的完成之一个过程,需要以下以下条件:
1.SNS系统之中,对每一个APP都有一个固定不变的网址,这个条件通常是具备的,因为SNS网站考虑到用户可能通过网址的复制来分享应用,所以这个网址肯定存在;
2.打开这个网址,就会直接转向到登录的页面。要是哪个SNS网站允许未登录用户查看某个APP的简单信息,需要用户手工点击一个“登录”按钮什么的才能进入登录过程,这样的话需要用户多点击一次,就使登录过程不怎么顺畅了;
3.用户登录完成之后,应该回到APP的页面,要是哪个SNS网站在登录之后总是到用户的控制台,而不是返回到登录请求页,那就不行了;
4.加载一个APP最好是在当前页面上加载,而不是在Iframe上加载,假如确实是在Iframe上加载的(目前最多的就是这种模式),也希望是在同一个域,实在不行是同一个根域也行,这是为了能够在获取到用户信息之后让网页的顶端框架跳转到登录系统,如果实在不能通过JS来进行转向,那就只能放target="_top"的链接提供给用户点击,那样的话,登录的流畅性也就大打折扣了。
能满足上面的四个条件,再加上SNS的APP通常都是能够获取用户的资料的,这样就可以比较流畅的融入到Step1登录系统之中,根据我使用的情况,校内网是能够实现比较好的登录过程的,下面我来介绍一下实现的具体过程(校内网有两种接口,我是采用Google提供标准的OpenSocial接口实现的):
1.首先还是要去校内网申请一个应用程序,在"开发者应用"之中点击"申请OpenSocial开发许可证",就会进入开发许可证申请页面:
注册完成之后,就得到了一个网址http://apps.xiaonei.com/passport/login.html,这个就是我们对用户进行转向的登录地址,转向过去之后,用户就开始了登录过程,输入帐号和密码之后,就进入了APP显示页面,APP先去获取用户信息,然后对用户进行转向,转向的过程大致如图:
在之后就会转向到后台的Handler页面,这个页面简单的将用户信息写入到Cookie,就完成了整个登录过程。
下面会逐步的显示实现的代码,因为这个比较特殊,代码分为4块:Web.config配置代码,OpenSocial App页面ASPX,OpenSocial App页面
类,和OpenSocialServer.cs代码
1.Web.Config配置代码:
1 <AccountServer name="xiaonei.com" type="Step1.AccountServer.AccountServers.OpenSocialServer,Step1.AccountServer" loginUrl="http://apps.xiaonei.com/passport/login.html"/>
2.OpenSocial APP页面ASPX,在本系统之中被我部署到http://account.step1.cn/account/tools/opensocial.aspx(直接打开无效)
OpenSocia APP页面代码
1<%@ Page Language="C#" AutoEventWireup="true" Inherits="Step1.AccountServer.Tools.OpenSocialPage" ResponseEncoding="utf-8" ContentType="text/xml"%><?xml version="1.0" encoding="UTF-8" ?>
2<Module>
3 <ModulePrefs title="Step1 Account">
4 <Require feature="opensocial-0.8"/>
5 </ModulePrefs>
6 <Content type="html">
7 <![CDATA[
8 <br/><br/>
9 <center><a id="accountLink" target="_top"><h2>正在登录到<%=domain%></h2></a></center>
10<script language="javascript">
11 function request()
12 {
13 //请求用户信息
14 var idspec = opensocial.newIdSpec({"userId":"OWNER","groupId":"FRIENDS"});
15 var req = opensocial.newDataRequest();
16 req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), "get_owner");
17 req.send(response);
18 }
19 function response(dataResponse)
20 {
21 //读取用户信息并转向到URL
22 var owner = dataResponse.get('get_owner').getData();
23 gotoUrl('<%=handleUrl%>&id='+encodeURIComponent(owner.getId())+'&name='+encodeURIComponent(owner.getDisplayName()));
24
25 }
26 function gotoUrl(url)
27 { //转向到URL
28 document.getElementById("accountLink").firstChild.innerHTML="登录完成,请点击返回(5秒之后自动返回)";
29 document.getElementById("accountLink").href=url;
30 setTimeout(function(){top.location=url;},5000);
31 }
32 gadgets.util.registerOnLoadHandler(request);
33</script>
34 ]]>
35 </Content>
36</Module>
3.OpenSocial APP页面代码文件
OpenSocialPage.cs
1 public partial class OpenSocialPage : System.Web.UI.Page
2 {
3 protected string handleUrl,acc,domain;
4 protected void Page_Load(object sender, EventArgs e)
5 {//设置需要显示给APP的变量
6 BaseServer server = AccountHelper.getServerByName(Request["ass"]);
7 handleUrl = server.getHandleUrl();
8 domain = Configuration.Instance().rootUrl;
9 }
10 }
4.OpenSocialServer.cs
OpenSocialServer.cs
1 public class OpenSocialServer:BaseServer
2 {
3 private string loginUrl;
4 //采用Web.Config之中的XML节点作为构造函数参数
5 public OpenSocialServer(System.Xml.XmlNode node)
6 : base(node)
7 {
8 for (int i = 0; i < node.Attributes.Count; i++)
9 {
10 switch (node.Attributes[i].LocalName)
11 {
12 case "loginUrl":
13 loginUrl = node.Attributes[i].Value;
14 break;
15 }
16 }
17 }
18 public override string getLoginUrl()//直接将XML配置之中的登录URL返回
19 {
20 return loginUrl;
21 }
22 public override void parseHandle(HttpContext page)//处理回转请求
23 {
24 System.Collections.Specialized.NameValueCollection request = HttpUtility.ParseQueryString(page.Request.Url.Query, Encoding.UTF8);
25 string id = request["id"];
26 string name = request["name"];
27 AccountHelper.setUserInfo(id, name, this.name);//设置用户的Cookie
28 AccountHelper.returnOpener();//转向到开始请求登录时的页面
29 }
30 }