演练:实现可插入 SSO 提供程序

默认情况下,Microsoft Office SharePoint Server 2007 提供 Microsoft Single Sign-On (SSO) Service,用于存储和映射与第三方或后端系统建立连接时所使用的凭据。许多公司已开发了内部凭据存储系统,或者使用 Microsoft Single Sign-On Service 之外的其他解决方案。Office SharePoint Server 2007 提供一种称为可插入 SSO 的机制,作为在两个位置保持凭据映射的替代方法。利用此功能,可以为 Office SharePoint Server 2007 中的标准 SSO 提供程序指定备用 SSO 提供程序。

先决条件

生成 SSO 提供程序之前必须设置环境。此演练假定已设置了 Office SharePoint Server 2007、安装了从 Microsoft 下载中心(该链接可能指向英文页面)下载的 AdventureWorks 2000 数据库副本,并已确保域名为 LITWAREINC。如果使用的是其他域名,则必须调整此演练中的代码示例。

假定已存在下表中显示的域帐户和组。

ExternalPartners

域组

InternalSales

域组

Tom Tompson

域用户

Jerry Jones

域用户

InternalAccess

域用户

ExternalAccess

域用户

有关如何设置数据库和必要的用户帐户的完整说明,请参阅随 AdventureWorks 2000 数据库提供的 README.txt 文件。

实现单一登录提供程序

替换 Office SharePoint Server 2007 中默认 SSO 提供程序的过程包括实现 Microsoft.SharePoint.Portal.SingleSignon.ISsoProvider接口、将其安装到全局程序集缓存以及向 Office SharePoint Server 2007 注册新的 SSO 提供程序。

只能向 Office SharePoint Server 2007 注册一个 SSO 提供程序。注册新的 SSO 提供程序将替换 Office SharePoint Server 2007 中的默认 SpsSsoProvider 类。由于一次只能使用一个 SSO 提供程序,因此建议在使用自定义 SSO 提供程序时停止 Microsoft Single Sign-On Service。

创建功能最简单的 SSO 提供程序将需要实现 ISsoProvider 接口的 GetCredentialsGetSsoProviderInfo 方法。本演练介绍如何创建简单的 SSO 提供程序以及使用它通过业务数据目录访问数据。

在本演练中,自定义 SSO 提供程序将 InternalSales 组中的用户映射到 InternalAccess 用户帐户,以便检索 AdventureWorks 2000 数据库中的产品数据。

生成提供程序

本节介绍如何生成并注册简单的 SSO 提供程序,并说明提供程序的异常处理。

下载 若要下载示例提供程序,请参阅 SharePoint Server 2007:软件开发工具包

示例

通过创建类库项目,在 Microsoft Visual Studio 2005 中创建 SSO 提供程序程序集。向该项目添加对 Microsoft.SharePoint.Portal.SingleSignon.dll(位于 %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\12\ISAPI 目录)的引用。实现 ISsoProvider 接口,如下例所示。

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Services;
using Microsoft.SharePoint.Portal.SingleSignon;

namespace SampleSSOProvider
{
    /// <summary>
    /// SimpleSSOProvider
    /// </summary>
    public class SimpleSSOProvider: ISsoProvider
    {
        public Application.ApplicationInfo[] GetApplicationDefinitions
        {
            //NOTE: Used by SpsSsoProvider, not necessary for SimpleSSOProvider
            throw new NotSupportedException;
        }
        public Application.ApplicationField[] GetApplicationFields(string AppID)
        {
            //NOTE: Used by SpsSsoProvider, not necessary for SimpleSSOProvider
            throw new NotSupportedException;
        }

public Application.ApplicationInfo GetApplicationInfo(string AppID)
        {
            Application.ApplicationInfo applicationInfo = new Application.ApplicationInfo("SimpleSSOProvider", "SimpleSSOProvider", Application.ApplicationType.GroupWindows, "sso@litwareinc.com");

            Application.ApplicationInfo applicationInfo = new Application.ApplicationInfo("SimpleSSOProvider","SimpleSSOProvider",Application.ApplicationType.GroupWindows,"sso@litwareinc.com", (SsoCredentialContents)((Int32)SsoCredentialContents.UserName + (Int32)SsoCredentialContents.Password + (Int32)SsoCredentialContents.WindowsCredentials));
            */

            return applicationInfo;
        }
        public Uri GetCredentialManagementURL(string AppID)
        {
            //NOTE: Used by SpsSsoProvider, not necessary for SimpleSSOProvider
            throw new NotSupportedException;
        }
        public SsoCredentials GetCredentials(string AppID)
        {
            //Note: Used by SpsSsoProvider, necessary for any SimpleSSO Provider. Implementation discussed in detail in the next section of this topic
        }
        public SsoCredentials GetCredentialsUsingTicket(string Ticket, string AppID)
        {
            //NOTE: Used by SpsSsoProvider, necessary for Simple SSO Provider when used by Excel Services.
            //TODO: Implement Ticket management code; currently just return SsoCredentials
            return GetCredentials(AppID);
        }
        public string GetCurrentUser
        {
            //NOTE: Used by SpsSsoProvider, not necessary for SimpleSSOProvider
            throw new NotSupportedException;
        }

public SsoCredentials GetSensitiveCredentials(string AppID)
{
    //NOTE: Used by SpsSsoProvider, necessary for Simple SSOProvider when used by Excel Services

    //TODO: Implement Sensitive Credential method, for sample just returning basic credentials
    return GetCredentials(AppID);
}
        public SsoProviderInfo GetSsoProviderInfo
        {
            //TODO: Used by SpsSsoProvider, necessary for any SimpleSSOProvider
        }
        public string GetTicket
        {
            //NOTE: Used by SpsSsoProvider, necessary for SimpleSSOProvider when used by Excel Services
            //TODO: Implement Ticket management code; currently just return a string
            return "No Ticket Management";
        }
        public void PutIdentityOnRequest(ref System.Web.Services.Protocols.HttpWebClientProtocol request, string AppID)
        {
            //NOTE: Used by SpsSsoProvider, not necessary for SimpleSSOProvider
            throw new NotSupportedException;
        }
        public void PutIdentityOnRequestUsingTicket(ref System.Web.Services.Protocols.HttpWebClientProtocol request, string Ticket, string AppID)
        {
            //NOTE: Used by SpsSsoProvider, not necessary for SimpleSSOProvider
            throw new NotSupportedException;
        }
    }
}

至少必须实现 GetCredentialsGetSsoProviderInfo 方法。所创建的 SimpleSSOProvider 类根据当前用户以及所提供的应用程序标识符 (AppID) 返回新的凭据。可以使用正在执行的线程的 CurrentPrincipal 属性 (System.Threading.Thread.CurrentPrincipal) 获取有关当前用户的信息。以下代码介绍 GetCredentials 方法的实现。

public SsoCredentials GetCredentials(string AppID)
{
    //NOTE: Used by SpsSsoProvider, necessary for any SimpleSSOProvider
    System.Diagnostics.Trace.WriteLine("Entering SimpleSSOProvider::GetCredentials");
    System.Diagnostics.Trace.Indent;
    // Retrieve the logged in user's information
    string domain = System.Environment.UserDomainName;
    System.Diagnostics.Trace.WriteLine("User domain is " + domain);
    
    try {
        System.Diagnostics.Trace.WriteLine("Context user:" + System.Threading.Thread.CurrentPrincipal.Identity.Name);
        // Start building an SsoCredentials object to store two values - UserName and Password
        SsoCredentials creds = new SsoCredentials;
        creds.Evidence = new System.Security.SecureString[2];

        switch (AppID){
            case "AdventureWorks":
                System.Diagnostics.Trace.WriteLine("Application is AdventureWorks");

                if (System.Threading.Thread.CurrentPrincipal.IsInRole("InternalSales"))
                {
                    System.Diagnostics.Trace.WriteLine("User is in InternalSales? " + System.Threading.Thread.CurrentPrincipal.IsInRole("InternalSales"));
                    // Provide components for the InternalAccess account token
                    creds.Evidence[0] = MakeSecureString(domain + "\\InternalAccess");
                    creds.Evidence[1] = MakeSecureString("pass@word1");
                }
                else
                {
                    // Provide components for the ExternalAccess account token
                    creds.Evidence[0] = MakeSecureString(domain + "\\ExternalAccess");
                    creds.Evidence[1] = MakeSecureString("pass@word1");
                }
                break;

            default:
                throw new SingleSignonException(SSOReturnCodes.SSO_E_APPLICATION_NOT_FOUND);
        }

        // Put the UserName/Password values into the credential object
        creds.UserName = creds.Evidence[0];
        creds.Password = creds.Evidence[1];

        System.Diagnostics.Trace.Unindent;
        return creds;
    }
    catch(SingleSignonException ex) {
        System.Diagnostics.EventLog.WriteEntry("SimpleSSOProvider", "Caught SSO Exception: " + ex.ToString);
        throw;
    }
    catch(Exception ex) {
        System.Diagnostics.EventLog.WriteEntry("SimpleSSOProvider", "Caught Exception: " + ex.ToString);
        throw new SingleSignonException(SSOReturnCodes.SSO_E_EXCEPTION, ex);
    }
}

SsoProvider 实现不需要 SsoCredentialsContents,但某些其他客户端应用程序可能需要 SsoCredentialsContents。在所提供的示例中,Excel Services 将尝试通过 Windows 登录使用已设置的 UserName 和 Password 连接到资源。如果尚未提供 WindowsCredentials 的值,则将在连接字符串中设置 UserName 和 Password。

名称

说明

未提供证据。

UserName

UserName 存在时设置。

Password

Password 存在时设置。

Evidence

使用扩展字段(合计最多五个,包括 UserName 和 Password)时设置。

MappedGroup

应用程序定义为 Group 定义时设置。

WindowsCredentials

应用程序定义为 Windows 身份验证时设置。

GetSsoProviderInfo 方法仅返回有关提供程序的信息,如 Vendor 名称和 Version,如下面的代码所示。

        public SsoProviderInfo GetSsoProviderInfo
        {
            //NOTE: Used by SpsSsoProvider, necessary for any SimpleSSOProvider
            SsoProviderInfo ssoProvInfo = new SsoProviderInfo;

            ssoProvInfo.AssemblyName = Assembly.GetExecutingAssembly.FullName;
            ssoProvInfo.Vendor = "AdventureWorks";
            ssoProvInfo.Version = "1.0";            

            return ssoProvInfo;
        }

如果 Excel Services 将使用 SSO 提供程序,则还必须提供 GetCredentialsUsingTicketGetTicket 方法的实现。

我们创建的 SimpleSsoProvider 类是 SSO 提供程序的一个非常简单的示例。实际的实现必须从安全库检索凭据,并保护内存中存储的任何值。

GetCredentials 返回的 SsoCredentials 对象使用 SecureString 类存储 UserName 和 Password 属性以及所有 Evidence 值。SecureString 对其数据进行加密,使这些数据不会被轻易解密。

异常处理

如果我们的 SimpleSSOProvider 无法正确地确定 AppID,则引发 SingleSignonException 的实例,并使用标准 SSOReturnCodes 字段。下表显示了用于几种错误情况的一些常见 SSOReturnCodes 字段。

名称

说明

SSO_E_ACCESSDENIED

访问被拒绝。

SSO_E_CREDS_NOT_FOUND

找不到被请求的用户或应用程序对应的凭据。

SSO_E_SSO_NOT_CONFIGURED

未正确配置 SSO 提供程序服务。

SSO_E_APPLICATION_NOT_FOUND

无法找到应用程序定义。

SSO_E_EXCEPTION

SSO 提供程序服务引发了异常。

注册提供程序

若要安装 SimpleSSOProvider,必须在全局程序集缓存中将其注册,然后用 ProviderAdmin 控制台应用程序(位于 Office SharePoint Server 2007 安装目录的 bin 目录中)将其注册。ProviderAdmin 应用程序将当前的 SSO 提供程序替换为您所指定的 SSO 提供程序。在服务器场环境中,必须向场中的每台计算机都注册新的 SSO 提供程序。以下过程介绍如何注册提供程序,以及如何删除自定义提供程序并重新启用原有的提供程序。

注册 SimpleSSOProvider

  • ProviderAdmin 工具采用完全限定的程序集名称以及实现 ISsoProvider 接口的类的名称。为了注册本示例中的 SimpleSSOProvider,ProviderAdmin 工具执行以下命令。

    Microsoft.SharePoint.Portal.SingleSignon.ProviderAdmin.exe 
    "SampleSSOProvider, Version=1.0.0.0, Culture=neutral, 
    PublicKeyToken=e447e624e7099fd1" 
    "SampleSSOProvider.SimpleSSOProvider"
    

删除自定义 SSO 提供程序,并重新启用原有的 SSO 提供程序。

  • 若要删除自定义 SSO 提供程序并重新启用 Office SharePoint Server 2007 中原有的 SSO 提供程序,请使用以下命令注销 SSO 提供程序。

    Microsoft.SharePoint.Portal.SingleSignon.ProviderAdmin.exe /u
    

访问单一登录提供程序

需要访问 SSO 提供程序的 Web 部件或其他组件不应再使用 Credentials 对象。使用 Credentials 对象只能检索随 Office SharePoint Server 2007 提供的默认 SSO 提供程序,即使已使用 ProviderAdmin 工具注册新的提供程序也是如此。若要获取对当前注册的 ISsoProvider 的引用,请使用以下过程。

获取对当前注册的 SSO 提供程序的引用

SsoCredentials 类通过 SecureString 类提供对凭据的访问。可以使用多种不同的方法(如 SecureStringToBSTR 方法)将 SecureString 实例转换为可用格式,如下例所示。

ISsoProvider provider = SsoProviderFactory.GetSsoProvider;
SsoCredentials creds = provider.GetCredentials("AdventureWorks");
IntPtr pUserName = IntPtr.Zero;
try
{
pUserName = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(creds.UserName);
//NOTE: After this has been converted to a String object, it remains in 
//memory until the garbage collector collects it.
String userName = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(pUserName);
}
finally
{
// Free zero out and free the BSTR pointers.
if (IntPtr.Zero != pUserName)
{
System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(pUserName);
}
}

从业务数据目录中使用单一登录提供程序

除了从 Web 组件可以访问默认 SSO 提供程序之外,还可以从已注册的业务数据目录应用程序中使用自定义 SSO 提供程序。

从业务数据目录中使用自定义 SSO 提供程序

  • 若要从数据库业务线 (LOB) 系统中使用自定义 SSO 提供程序,请修改业务数据目录架构,以便将 SsoApplicationId 和 SsoProviderImplementation 属性添加到 LOBSystemInstance XML 标记,如下所示。

    <LobSystemInstance Name="AdventureWorks2000(SampleSSO)">
    <Properties>
    <Property Name="AuthenticationMode" Type="System.String">WindowsCredentials</Property>
    <Property Name="SsoApplicationId" Type="System.String">AdventureWorks</Property>
    <Property Name="SsoProviderImplementation" Type="System.String">SampleSSOProvider.SimpleSSOProvider, SampleSSOProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e447e624e7099fd1</Property>
    
    <!—Database connection properties elided -->
    </Properties>
    </LobSystemInstance>
    

由于提供程序返回 Windows 凭据,因此将 AuthenticationMode 属性设置为接收 WindowsCredentials。当业务数据目录从 SSO 提供程序检索凭据时,前者将在尝试访问数据库之前执行 LogonUser 调用以建立模拟。

在本示例中,用户被映射到 InternalAccess 或 ExternalAccess 帐户,以便从 AdventureWorks 2000 数据库检索 Product 数据。

有关业务数据目录架构的详细信息(包括为 Web 服务 LOB 系统实现 SSO 所需的配置),请参阅业务数据目录:元数据模型中的 LobSystemInstance

Next Steps

由于可以替换默认 Office SharePoint Server 2007 SSO 提供程序,因此可以将 SharePoint 网站与企业中现有的投资更好地结合。可以利用内部开发或作为第三方程序包的一部分提供的现有凭据存储区。然后可以从 Web 部件或业务数据目录对象访问自定义提供程序,从而充分利用自定义 SSO 提供程序。