SharePoint Portal Server 2003提供了一个看上去很酷的功能:单点登陆(Single Sign On)。
SPS可以为网站用户记录其在其他应用系统中的登陆凭据,而我们的程序(WebPart)可以通过获取并利用这些凭据代替用户登陆第三方应用系统,并输出所需要展示的数据。
其工作原理大致如下:
1、用户访问网站上的单点登陆WebPart
2、WebPart通过Single Sign On的对象模型从SSO数据库中查找当前用户的凭据
3、如果没有此凭据,则将用户导引至输入凭据的页面
4、如果有此凭据,则通过此凭据登陆第三方应用系统,获取相关业务数据
有关Single Sign On的管理和使用,可以参考kaneboy的Webcast,并且有相关的源代码下载。
这篇文章中主要介绍的内容是上述工作原理的第三步:用户输入凭据的页面。
如果使用默认的输入页面(用如下代码获得URL):
用户将在这样的页面中输入登陆凭据:
这个页面是一个典型的sharepoint的_layouts目录.NET应用页面。
就个人而言,我很喜欢这样的页面风格,但是我们还是会面对修改这个页面的需求,即我们需要用自己开发的页面引导用户输入其应用凭据。
那么,如何自己编写实现此功能的页面呢?接下来是一个实例:
1、打开vs2003,在http://localhost/_layouts/SSOSample下创建一个ASP.NET的web应用程序。(当然你要记得把“Localhost”换成你的SharePoint站点地址)
2、创建一个新的用户控件
3、在用户控件中拖入几个必需的web控件:
如上图,两个TextBox,一个按钮,以及一个显示错误信息的Label。
同时,为了向那些已经拥有凭据的用户隐藏输入框,我们把前3个控件包含在一个Panel中,并另这个Panel默认隐藏。
4、编写“保存”按钮的响应函数:
{
if (this.tbUserName.Text == null || this.tbUserName.Text =="")
{
this.lbOutput.Text = "请输入用户名";
return;
}
if (this.tbPassword.Text == null || this.tbPassword.Text =="")
{
this.lbOutput.Text = "请输入登陆密码";
return;
}
string[] rgSetCredentialData = new string[2];
rgSetCredentialData[0] = this.tbUserName.Text;
rgSetCredentialData[1] = this.tbPassword.Text;
try
{
Credentials.SetCredentials(1, "SSOTEST", rgSetCredentialData);
}
catch(SingleSignonException esso)
{
this.lbOutput.Text = esso.Message + "<br>" + esso.StackTrace;
}
this.Page.Response.Redirect(this.Page.Request.Url.ToString());
}
为了演示,我在Page_Load函数中加入如下代码,使得已经填写SSO凭据的用户可以浏览到自己凭据内容:
{
// 在此处放置用户代码以初始化页面
try
{
String[] asCredData = null;
Credentials.GetCredentials(1, "SSOTEST", ref asCredData);
String sUsername = asCredData[0];
String sPassword = asCredData[1];
this.lbOutput.Text = sUsername + "<br>" + sPassword;
}
catch (SingleSignonException ex)
{
if (SSOReturnCodes.SSO_E_CREDS_NOT_FOUND == ex.LastErrorCode)
{
if (!this.IsPostBack)
{
this.tbPassword.Text = "";
this.tbUserName.Text = "";
this.pnlSSOPart.Visible = true;
}
else
{
this.lbOutput.Text = "请重新填写您的帐户信息";
}
}
}
}
5、一切并没有到此结束,如果直接编译运行,我们在点击按钮的时候会收到这样的出错信息:
at Microsoft.SharePoint.Portal.SingleSignon.SSOCanaryChecker.b() at Microsoft.SharePoint.Portal.SingleSignon.SSOCanaryChecker.b() at Microsoft.SharePoint.Portal.SingleSignon.Credentials.SetCredentials(UInt32 ui32Flags, String strApplicationName, Object[] Args)
这是因为页面安全性的问题。我们需要在页面中引入SharePoint的FormDigest控件和SSOFormDigest控件:
即在用户控件的ascx文件中加入:
<%@ Register TagPrefix="cc1" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<cc2:formdigest id="FormDigest1" runat="server"></cc2:formdigest>
<cc1:ssoformdigest id="SSOFormDigest1" runat="server"></cc1:ssoformdigest>
这个解决办法,尤其是引入SSOFormDigest控件,我是从以下文章找到的:
http://geekswithblogs.net/tariq/archive/2004/09/10/10961.aspx
当然,我是从GOOGLE中搜索得到这一文章的。
插句题外话,我非常赞同熊明峰同志的观点:在使用GOOGLE中一定要使用恰当的搜索关键词,才能获得最有效率的搜索结果,所以我希望大家都能分享自己搜索到可用信息时使用的关键词,来提高自己的搜索技能。也许并不遥远的以后,选择关键词的能力将成为人类重要技能之一。
我使用的关键词:sharepoint SetCredentials
6、我们的用户SSO凭据存储功能基本就完成了:
一个没有凭据的用户登陆此页面时,需要填写新的用户凭据:
一旦保存成功,用户再次登陆的时候,SPS就能直接返回其对应的用户凭据内容:
好。接下来你就可以躲到写字楼的安全通道抽支烟,把剩下的工作丢给美工了。
至少我是这么干的。