概述
大概花了两个下午加一个晚上完成了对mojoPortal 项目的新浪微博登录的集成。主要时间并非花在如何使用新浪微博登录这个工作上,这一方面在应该说本身不存在太大的问题。而花了这么长时间的确出乎我的意料,再回过头来看看这个过程,主要是花费了大量时间来研究mojoPortal项目的组织结构上面。由于之前也没仔细研究mojoPortal项目的结构所以集成起来的确会慢很多。确实网上对于这个项目的介绍或者开发方式涉及的并不多,因此,我把这个过程记录下来,如果有使用mojoPortal项目的同学,可以节省很多时间。
目标
我们在使用mojoPortal项目的时候,可以看到SiteSettings.aspx页面的设置中其实已经有第三方集成登录的选项了,如图2.1所示:
图 2.1 Windows Live登录设置
我们只需要把Windows Live 提供给我们的App Key填进去,然后允许认证就好啦,很方便吧!我们要做的就是加一个新浪微博的选项卡如下:
图 2.2 新浪微博登录设置
这样,我们也可以打个勾填个Key和Secret就能开启新浪微博登录了,也可以很方便的关闭新浪微博登录,酷!当我们开启选项后,就可以在登录页面看到新浪微博登录的链接了,如下图2.3:
图 2.3 登录页面
通过新浪微博登录后就可以到我们自己的网站上去啦,如果是第一次从第三方登录,则新建一个系统的账户并把它与第三方账号联系起来(即使通过授权也无法获取新浪微博的注册邮箱,因此不得不在用户第一次连接我们网站时要求填写邮箱)。
实现
a) 添加新浪微博设置选项卡
b) 存储信息到数据库
c) 添加新浪登录按钮
d) 新浪验证回调页面处理
e) 创建第三方登录补充页面
a) 添加新浪微博选项卡
首先找到网站设置页面SiteSettings.aspx,在mojoPortal.Web项目的Admin目录下。根据图2.1与2.2中所示,第三方登录包含于安全选项卡中,而页面的整个选项卡结构如下图所示:
图 3.1 设置页面的选项卡
很快我们可以定位到id为tabSecruity的层中,它是由头部的子选项卡与每个选项卡对应的设置选项组成的。我们很清楚的看到上面的一个li列表项对应下面的一个div层,如图3.2,我们对着Windows Live列表项与选项卡依葫芦画瓢,这样就可以得到图3.3的样子。
图 3.2 安全选项卡中全貌
图 3.3 添加新浪微博登录后的安全选项卡
很简单,不是吗?下图是新浪微博展开代码:
图 3.4 新浪微博详细设置项
我们再来看一下代码中的属性。
- mojoPortal项目中class值为settingrow的div即一个设置项,每一个设置项包含一个Label在和一个Input(CheckBox, TextBox等等),如果有必要,还会包含一个查看帮助的小问号;
- <mp:SiteLabel>是一个用户自定义控件,我们查看页面头部并未发现有注册tagPrefix 为mp的条目,因此它应该是全局的。我们打开Web.Config可以看到<controls>选项卡中有一个相关条目:
<add tagPrefix="mp" namespace="mojoPortal.Web.Controls" assembly="mojoPortal.Web.Controls" />
这里显然指定了mojoPortal.Web.Controls 程序集,我们到对应项目中查找确实有名为SiteLabel的类。些类并未做太多工作,主要是ConfigKey属性作为资源文件里面的Key来获取本地语言版本; - <portal:mojoHelpLink> 即为帮助链接,同样的方法在mojoPortal.Web项目中Controls文件夹中找到对应类。
b) 存储信息到数据库
接下去要做的就是把第三方平台的配置信息存入数据库,我们在mp_sites数据表中找到有WindowsLiveAppID和WindowsLiveKey字段,同样我们也把新浪的AppKey及AppSecret存储到这个表中,别忘记了添加是否允许新浪微博注册的字段。数据库更新脚本我就不贴了,另外改动表结构之后,会涉及到数据访问层及业务层的更改,主要是SiteSettings这个类,这项工作很简单完全不费脑子!改好后去后台页面找到PopulateLabels()及LoadSettings()函数,顾明思义,作了些把标签进行本地化显示和加载设置的工作,添加以下几个地方:第一、找到 liWindowsLive.Visible=false;这一行并在下面添加两行 liSinaWeibo.Visible = false; tabSinaWeibo.Visible = false;其中 liSinaWeibo及tabSinaWeibo为前面页面中定义的列表项及选项卡的ID;
第二、找到 if (WebConfigSettings.EnableWindowsLiveAuthentication)这一行,以同样的形式添加语句块:if (WebConfigSettings.EnableSinaWeiboAuthentication) { chkAllowSinaWeiboAuth.Checked = selectedSite.AllowSinaWeiboAuth; txtSinaWeiboAppKey.Text = selectedSite.SinaWeiboAppKey; txtSinaWeiboAppSecret.Text = selectedSite.SinaWeiboAppSecret; }并在最后的else中加入chkAllowSinaWeiboAuth.Checked = false; chkAllowSinaWeiboAuth.Enabled = false;其中三个对象看名字就明白了。selectedSites是SiteSettings对象,它是当前选择的站点设置,这里可以直接获取里面的三个刚才添加的属性,这两个语句块主要检测Web.Config文件中是否允许新浪微博登录如果允许即把数据库值绑定到控件,因此我们必须在Web.Config文件中找到EnableOpIDAuthentication在其下面加入如下节点:<add key="EnableSinaWeiboAuthentication" value="true"/>为了能够使程序获取到配置项,我们还要更改WebConfigSettings类,它在mojoPortal.Web项目的Components文件夹中,我们要为类实现一个get访问器:public static bool EnableSinaWeiboAuthentication { get { return ConfigHelper.GetBoolProperty("EnableSinaWeiboAuthentication", false); } }第三、找到litWindowsLiveTabLink.Text = "<a href='#" + tabWindowsLiveID.ClientID + "'>"+ Resource.SiteSettingsSecurityWindowsLiveTab + "</a>";在其后面添加语句:litSinaWeiboTabLink.Text = "<a href='#" + tabSinaWeibo.ClientID + "'>" + Resource.SiteSettingsSecuritySinaWeiboTab + "</a>";这句话主要从资源文件加载本地化的标签名字,我们需要在资源文件中添加相应的值,这个就不详说了。第四、在btnSave_Click事件中加入如下代码块:if (WebConfigSettings.EnableSinaWeiboAuthentication) { selectedSite.AllowSinaWeiboAuth = chkAllowSinaWeiboAuth.Checked; selectedSite.SinaWeiboAppKey = txtSinaWeiboAppKey.Text; selectedSite.SinaWeiboAppSecret = txtSinaWeiboAppSecret.Text; }这样,我们在文本框中填入的数据就可以保存到数据库啦啦!快申请一下新浪微博的AppKey吧。
c) 编写新浪登录控件
在mojoPortal.Web项目中Controls文件夹下添加SinaWeiboLoginControl.ascx,前台页面结构如图3.5所示,其中三个地方需要后台加载,是否使用https协议,新浪给的AppKey及回调地址。由于mojoPortal项目中把页面的AutoEventWireup属性都设置成了false,因此我们就入乡随俗吧,同样把SinaWeiboLoginControl.ascx控件的这个属性设置为false,并在后台手动把事件加入事件链。
图 3.5 新浪登录用户自定义控件
控件的后台代码实现思路与项目中的WindowsLiveLoginControl.ascx控件一样,先写一个Page_Load函数,在页面OnInit时把Page_Load放到base.Load事件上即可,Page_Load完成的工作是 加载前台需要的三个参数->处理返回地址(这其中的CallBackUrl页面我们在 d) 步骤中添加)。Page_Load与LoadSettings具体代码实现如下:
protected void Page_Load(object sender, EventArgs e) { LoadSettings(); if ( (!WebConfigSettings.EnableSinaWeiboAuthentication) || (!siteSettings.AllowSinaWeiboAuth) ) { this.Visible = false; return; } //处理回调地址 callbackUrl = SiteUtils.GetNavigationSiteRoot() + "/Secure/SinaWeiboAuthHandler.aspx"; if (callbackUrl.StartsWith("http://") &&protocol.StartsWith("https://") ) callbackUrl.Replace("http://", protocol); if (!IsPostBack) { /*设置返回路径*/ string returnUrl = WebConfigSettings.PageToRedirectToAfterSignIn; if (returnUrl.EndsWith(".aspx")) { CookieHelper.SetCookie(returnUrlCookieName, returnUrl); return; } if (Page.Request.UrlReferrer != null) { returnUrl = Page.Request.UrlReferrer.ToString(); } string returnUrlParam = Page.Request.Params.Get("returnurl"); if (!String.IsNullOrEmpty(returnUrlParam)) { returnUrl = Page.ResolveUrl(Page.Server.UrlDecode(returnUrlParam)); } if (returnUrl.Length > 0) { CookieHelper.SetCookie(returnUrlCookieName, returnUrl); } } }/// <summary> /// 加载设置项 /// </summary> protected void LoadSettings() { siteSettings = CacheHelper.GetCurrentSiteSettings();//加载网站配置 if (SiteUtils.SslIsAvailable()) protocol = "https://"; else protocol = "http://"; /*全局配置优先,如果无全局配置则使用当前站点的"新浪"的配置*/ sinaWeiboAppKey = siteSettings.SinaWeiboAppKey; sinaWeiboAppSecret = siteSettings.SinaWeiboAppSecret; if (ConfigurationManager.AppSettings["GlobalSinaWeiboAppKey"] != null) { string globalSinaWeiboAppKey=ConfigurationManager.AppSettings["GlobalSinaWeiboAppKey"].Trim(); if (globalSinaWeiboAppKey.Length > 0) sinaWeiboAppKey = globalSinaWeiboAppKey; } if (ConfigurationManager.AppSettings["GlobalSinaWeiboAppSecret"] != null) { string globalSinaWeiboAppSecret =ConfigurationManager.AppSettings["GlobalSinaWeiboAppSecret"].Trim(); if (globalSinaWeiboAppSecret.Length >= 0) sinaWeiboAppSecret = globalSinaWeiboAppSecret; } if (sinaWeiboAppKey.Length == 0 || sinaWeiboAppSecret.Length == 0) this.Visible = false; }
d) 添加新浪登录按钮
查看mojoPortal.Web项目中Secure文件夹下的Login.aspx页面,结构如图3.6。其中包含了两个部分一个是所谓的 StardardLogin也就是系统自己的标准登录,另外的就是第三方登录了。图3.5中已把新浪微博控件添加到页面中,并在页面头部进行了注册如图3.7,加载的控件就是刚 b) 中编写的新浪登录控件。
图 3.6 登录页面结构
图 3.7 页面头部的注册信息
d) 新浪验证回调页面处理
用户在通过新浪的认证后,便会把access_token,uid等回传给之前设定的回调页面。在Secure文件夹下添加一个页面命名为SinaWeiboAuthHandler.aspx,我们把它作为回调页面。我们在通过新浪登录验证后就加载到如下的Url:
/Secure/SinaWeiboAuthHandler.aspx#access_token=2.00W88&expires_in=86400&remind_in=54572&uid=1299708822
很不幸的是,这里用的是“#”而不是“?”,因此,我们在后台是无法直接获取到后面的参数的,我们只能用javascript来把参数过虑出来,再提交给后台做想做的事情。js代码很简单,就不贴了。而后台需要判断本次登录的用户是本站新用户还是老用户,如果是新用户,就让用户去RegisterWithSinaWeiboID.aspx(在步骤 e) 中建立)填邮箱;否则登录成功直接跳转。后台的Page_Load代码如下:
protected void Page_Load(object sender, EventArgs e) { LoadParams(); if (urlParams.Count > 0 && urlParams["accessToken"] != null && urlParams["uid"] != null ) { weiboCookieName = "sinaWeibo" + siteSettings.SiteId.ToString(CultureInfo.InvariantCulture); CookieHelper.SetCookie(weiboCookieName, urlParams["accessToken"]); Guid userGuid = SiteUser.GetUserGuidFromSinaWeiboId(siteSettings.SiteId , urlParams["uid"]); SiteUser usr = new SiteUser(siteSettings,userGuid); if(userGuid==Guid.Empty) WebUtils.SetupRedirect( this, SiteUtils.GetNavigationSiteRoot() + "/Secure/RegisterWithSinaWeiboID.aspx?uid=" + urlParams["uid"]); else { FormsAuthentication.SetAuthCookie(usr.Name, false); WebUtils.SetupRedirect(this, SiteUtils.GetNavigationSiteRoot()); } } }
e) 创建第三方登录补充页面
前面提到,由于无法获取到用户邮箱地址,我们必须在用户第一次登录时迫使他们去填写邮箱。我们创建RegisterWithSinaWeiboID.aspx页面来完成这项工作。到这一步时,我们开始使用新浪微博的SDK,我这里用的是 AMicroblogAPI 。注意到作者在其指导页面上有这样一个步骤:
图 3.8 作者要求的配置文件
很明显,我们无法满足这个要求,我们从数据库获取信息并非从配置文件,那如果配置文件中没有对应配置项,会抛出异常吗?带着这个疑问把源码下载后查看,果不其然,如果无法加载配置文件,刚会抛出异常,这并不是我们想要的。
static Environment() { var section = ConfigurationManager.GetSection("amicroblogAPI") as AMicroblogAPIConfigurationSection; if (null != section) { Environment.ResponseErrorHandlingEnabled = section.ResponseErrorHandlingConfig.Enabled; Environment.Configuration = section; foreach (HandlerConfigurationElement element in section.ResponseErrorHandlingConfig) { if(string.IsNullOrEmpty(element.Type)) throw new AMicroblogException( LocalErrorCode.ArgumentNotProvided, "Handler type not provided in responseErrorHandling configuration section." ); var type = Type.GetType(element.Type, true, true); var errorCode = element.ErrorCode; if (string.IsNullOrEmpty(errorCode)) errorCode = "*"; var interfaceName = "IResponseErrorHandler"; var inter = type.GetInterface(interfaceName, false); if (null == inter) throw new AMicroblogException( LocalErrorCode.ArgumentInvalid, "Type '{0}' does not implement {1}.", element.Type, interfaceName ); ResponseErrorHandlers.Add(new HandlerConfiguration() { Type = type, ErrorCode = errorCode }); } if (Environment.ResponseErrorHandlingEnabled) { Environment.ResponseError += new EventHandler<ResponseErrorEventArgs>(HandleResponseError); } } AppKey = ConfigurationManager.AppSettings["appKey"]; AppSecret = ConfigurationManager.AppSettings["appSecret"]; RedirectUri = ConfigurationManager.AppSettings["redirectUri"]; if (string.IsNullOrEmpty(AppKey) || string.IsNullOrEmpty(AppSecret)) { throw new AMicroblogException( LocalErrorCode.AppKeyOrSecretNotProvided, "appKey or appSecret not configured in application config file." ); } }
我们把最后抛出异常的语句注释掉即可,然后以Release模式重新编译一下,放到我们的项目的_lib文件夹中并添加引用。接下去就是通过API来获取一些想要的信息了,我这边新建一个SinaWeibo.cs类,作为新浪微博的操作辅助类,下面为构造函数:
public SinaWeibo(string accessToken,string uid,string sinaWeiboAppKey,string sinaWeiboAppSecret) { currentUserID = uid; OAuthAccessToken oat = new OAuthAccessToken(); oat.Token = accessToken; oat.UserID = uid; AMicroblogAPI.Environment.AccessToken = oat; if(string.IsNullOrEmpty(AMicroblogAPI.Environment.AppKey)) AMicroblogAPI.Environment.AppKey = sinaWeiboAppKey; if (string.IsNullOrEmpty(AMicroblogAPI.Environment.AppSecret)) AMicroblogAPI.Environment.AppSecret = sinaWeiboAppSecret; }
当然你也可以不用定义这样的类,直接使用他的API,如果不怕麻烦的话。我们在加载RegisterWithSinaWeiboID.aspx加载时从新浪获取指定用户的信息。再结合用户输入的邮箱就可以创建本站账户了。RegisterWithWeiboID.aspx页面后台代码直接参考RegisterWithWindowsLiveID.aspx即可。
总结
整个过程并无难点,细心即可。当然不用这种思路去实现也是完全可以的,但是我认为既然选用了mojoPortal项目,那么就按照作者给我们提供的思路去实现会使得项目更加自然,也便于整体的理解,最重要的是维护方便。今天花了比较多的时间写这么多文字,实属难得,倘若项目时间紧迫,恐怕也要像前几篇文章那样寥寥几句带过。无论如何,哪怕给个别同学带来益处,也是值得的,不是吗?