mojoPortal集成第三方登录

概述

大概花了两个下午加一个晚上完成了对mojoPortal 项目的新浪微博登录的集成。主要时间并非花在如何使用新浪微博登录这个工作上,这一方面在应该说本身不存在太大的问题。而花了这么长时间的确出乎我的意料,再回过头来看看这个过程,主要是花费了大量时间来研究mojoPortal项目的组织结构上面。由于之前也没仔细研究mojoPortal项目的结构所以集成起来的确会慢很多。确实网上对于这个项目的介绍或者开发方式涉及的并不多,因此,我把这个过程记录下来,如果有使用mojoPortal项目的同学,可以节省很多时间。

目标

我们在使用mojoPortal项目的时候,可以看到SiteSettings.aspx页面的设置中其实已经有第三方集成登录的选项了,如图2.1所示:

clip_image002

图 2.1 Windows Live登录设置

我们只需要把Windows Live 提供给我们的App Key填进去,然后允许认证就好啦,很方便吧!我们要做的就是加一个新浪微博的选项卡如下:


图 2.2 新浪微博登录设置

这样,我们也可以打个勾填个Key和Secret就能开启新浪微博登录了,也可以很方便的关闭新浪微博登录,酷!当我们开启选项后,就可以在登录页面看到新浪微博登录的链接了,如下图2.3:

clip_image006

图 2.3 登录页面

通过新浪微博登录后就可以到我们自己的网站上去啦,如果是第一次从第三方登录,则新建一个系统的账户并把它与第三方账号联系起来(即使通过授权也无法获取新浪微博的注册邮箱,因此不得不在用户第一次连接我们网站时要求填写邮箱)。

实现

a) 添加新浪微博设置选项卡

b) 存储信息到数据库

c) 添加新浪登录按钮

d) 新浪验证回调页面处理

e) 创建第三方登录补充页面

a) 添加新浪微博选项卡

首先找到网站设置页面SiteSettings.aspx,在mojoPortal.Web项目的Admin目录下。根据图2.1与2.2中所示,第三方登录包含于安全选项卡中,而页面的整个选项卡结构如下图所示:

clip_image008

图 3.1 设置页面的选项卡

很快我们可以定位到id为tabSecruity的层中,它是由头部的子选项卡与每个选项卡对应的设置选项组成的。我们很清楚的看到上面的一个li列表项对应下面的一个div层,如图3.2,我们对着Windows Live列表项与选项卡依葫芦画瓢,这样就可以得到图3.3的样子。

clip_image010

图 3.2 安全选项卡中全貌

clip_image012

图 3.3 添加新浪微博登录后的安全选项卡

很简单,不是吗?下图是新浪微博展开代码:

image

图 3.4 新浪微博详细设置项

我们再来看一下代码中的属性。

  1. mojoPortal项目中class值为settingrow的div即一个设置项,每一个设置项包含一个Label在和一个Input(CheckBox, TextBox等等),如果有必要,还会包含一个查看帮助的小问号;
  2. <mp:SiteLabel>是一个用户自定义控件,我们查看页面头部并未发现有注册tagPrefix 为mp的条目,因此它应该是全局的。我们打开Web.Config可以看到<controls>选项卡中有一个相关条目:
    <add tagPrefix="mp" namespace="mojoPortal.Web.Controls" assembly="mojoPortal.Web.Controls" />
         
    这里显然指定了mojoPortal.Web.Controls 程序集,我们到对应项目中查找确实有名为SiteLabel的类。些类并未做太多工作,主要是ConfigKey属性作为资源文件里面的Key来获取本地语言版本;
  3. <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,并在后台手动把事件加入事件链。

image

图 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) 中编写的新浪登录控件。

image

图 3.6 登录页面结构

image

图 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 。注意到作者在其指导页面上有这样一个步骤:

image

图 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项目,那么就按照作者给我们提供的思路去实现会使得项目更加自然,也便于整体的理解,最重要的是维护方便。今天花了比较多的时间写这么多文字,实属难得,倘若项目时间紧迫,恐怕也要像前几篇文章那样寥寥几句带过。无论如何,哪怕给个别同学带来益处,也是值得的,不是吗?

原文地址:https://www.cnblogs.com/mike442144/p/2439460.html