步骤 2:为自定义 Web 部件添加代码

备注

本主题介绍 Infrastructure Update for Microsoft Office Servers中的功能。若要下载此更新,请参阅 SharePoint Server 2007 基础结构更新说明:2008 年 7 月 15 日

此演练中所述的自定义联合搜索结果 Web 部件包括一个供用户输入用户凭据的用户界面 (UI)。Web 部件然后会将这些凭据传递给联合结果数据源。

此演练中所述的示例 Web 部件适用于被配置为使用下列每用户身份验证类型的联合位置:

  • 基本身份验证

  • 摘要式身份验证

  • NTLM 身份验证

凭据被加密,然后存储在 Cookie 中。Cookie 仅在用户浏览会话期间存在;关闭浏览器时,Cookie 将过期,并且不再可用。您可以修改此示例来扩展 Cookie 过期日期,以便保留这些值。

重要

在浏览器和服务器之间传递 Cookie 中的凭据时,建议您使用安全套接字层 (SSL) 来保护客户端浏览器和 Web 服务器之间的通信。有关详细信息,请参阅步骤 3:部署自定义 Web 部件中的“安全注意事项”。

您可以从自定义联合结果 Web 部件示例版本选项卡中下载自定义联合搜索结果 Web 部件示例的完整代码,此选项卡位于 MSDN 代码库Microsoft Office SharePoint Server 2007 SDK 搜索示例资源页上。

过程

修改 PerUserAuthWebPart 中的代码

  1. 在 PerUserAuthWebPart.cs 文件中代码顶端附近添加以下命名空间指令。

    using System.Xml.Serialization;
    using System.Xml.XPath;
    using System.Net;
    using System.Web.Security;
    using Microsoft.Office.Server.Search.WebControls;
    using Microsoft.Office.Server.Search.Administration;
    
  2. 在以下代码行中将 WebControl 替换为 FederatedResultsWebPart 和 IPostBackEventHandler:

    public class PerUserAuthWebPart : FederatedResultsWebPart, IPostBackEventHandler
    
  3. 在 PerUserAuthWebPart 的类声明上方添加以下代码行。

    [XmlRoot(Namespace = "CustomFederatedResultsSample")]
    

    将以下代码添加在类声明下方。

    private TextBox UsernameTextBox = new TextBox();
    private TextBox PasswordTextBox = new TextBox();
    private Button LogOnButton = new Button();
    private Button LogOutButton = new Button();
    private string Username = "";
    private string Domain = "";
    private string Password = "";
    private ICredentials Creds;
    XPathNavigator Results;
    Table controlsTable;
    Label statusLabel = new Label();
    TableRow LoginControlsRow;
    
  4. 使用以下代码替代 OnLoad、OnPreRender 和 CreateChildControls 方法。

    protected override void OnLoad(EventArgs e)
    {
        try
        {
            this.ShowMessages = false;
            base.OnLoad(e);
        }
        catch (Exception ex)
        {
            string x = ex.Message.ToString();
        }
    }
    
    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        base.CreateChildControls();
    }
    
    protected override void CreateChildControls()
    {
        this.CreateLogonControls();
    }
    
  5. 为 CreateLogonControls 方法添加以下代码,该方法将创建并加载用于请求用户凭据的自定义界面控件。

    protected void CreateLogonControls()
    {
        controlsTable = new Table();
        controlsTable.Width = Unit.Percentage(100);
        controlsTable.Attributes.Add("cellspacing", "1");
        controlsTable.Attributes.Add("cellpadding", "1");
        LoginControlsRow = new TableRow();
        TableRow LogoutControlsRow = new TableRow();
        TableCell cell = new TableCell();
        this.UsernameTextBox = new TextBox();
        this.UsernameTextBox.ID = "UserName";
        UsernameTextBox.Visible = true;
        UsernameTextBox.EnableViewState = true;
        cell.Controls.Add(UsernameTextBox);
        LoginControlsRow.Controls.Add(cell);
    
        cell = new TableCell();
        PasswordTextBox = new TextBox();
        this.PasswordTextBox.ID = "Password";
        PasswordTextBox.TextMode = TextBoxMode.Password;
        PasswordTextBox.Visible = true;
        PasswordTextBox.EnableViewState = true;
        cell.Controls.Add(PasswordTextBox);
        LoginControlsRow.Controls.Add(cell);
    
        cell = new TableCell();
        this.LogOnButton = new Button();
        LogOnButton.Text = "Logon";
        LogOnButton.Visible = true;
        cell.Controls.Add(LogOnButton);
        LoginControlsRow.Controls.Add(cell);
        LoginControlsRow.Width = Unit.Percentage(100);
    
        cell = new TableCell();
        statusLabel.Text = ctrl;
        cell.Controls.Add(statusLabel);
        TableRow statusRow = new TableRow();
        statusRow.Cells.Add(cell);
    
        cell = new TableCell();
        this.LogOutButton = new Button();
        LogOutButton.Text = "Logout";
        LogOutButton.Visible = true;
        cell.Controls.Add(LogOutButton);
        statusRow.Cells.Add(cell);
    
        controlsTable.Rows.Add(statusRow);
        controlsTable.Rows.Add(LoginControlsRow);
        this.Controls.Add(controlsTable);
    }
    
  6. 使用以下代码替代 ConfigureDataSourceProperties 方法。

    protected override void ConfigureDataSourceProperties()
    {
        base.ConfigureDataSourceProperties();
        FederatedResultsDatasource fedrds = this.DataSource as FederatedResultsDatasource;
        string locName = "";
        LocationConfiguration locationConfig = null;
        if (fedrds.Location != null)
        {
            locName = fedrds.Location;
            LocationConfigurationCollection locations = SearchContext.Current.LocationConfigurations;
            locations.TryGetLocationConfigurationByInternalName(locName, out locationConfig);
        }
    
        try
        {
        if (Page.Request.Cookies["Username"] != null)
        {
            Username = DecryptedCookieData(Page.Request.Cookies["Username"].Value);
        }
        if (Page.Request.Cookies["Domain"] != null)
        {
            Domain = DecryptedCookieData(Page.Request.Cookies["Domain"].Value);
        }
        if (Page.Request.Cookies["Password"] != null)
        {
            Password = DecryptedCookieData(Page.Request.Cookies["Password"].Value);
        }
        }
        catch (Exception e)
        {
            string exception = e.Message;
        }
    
        if (Page.IsPostBack)
        {
            if (GetPostBackControlText() == "Logout")
            {
                    Username = "";
                    Password = "";
                    Domain = "";
                }
            else if (GetPostBackControlText() == "Logon")
            {
                if (UsernameTextBox.Text.Contains(@"\"))
                {
                    string[] domainUsername = UsernameTextBox.Text.Split('\\');
                    Domain = domainUsername[0];
                    Username = domainUsername[1];
                }
                else
                {
                    Username = UsernameTextBox.Text;
                }
                Password = PasswordTextBox.Text;
            }
        }
    
        Page.Response.Cookies["Username"].Value = EncryptedCookieData(Username,"Username");
        Page.Response.Cookies["Password"].Value = EncryptedCookieData(Password, "Password");
        Page.Response.Cookies["Domain"].Value = EncryptedCookieData(Domain, "Domain"); 
    
        if (fedrds.Location != null)
        {
            Creds = new NetworkCredential(Username, Password, Domain);
        }
    
        if (fedrds.UserCredentials.ContainsKey(locName))
        {
            fedrds.UserCredentials.Remove(locName);
        }
        fedrds.UserCredentials.Add(locName, Creds);
    }
    
  7. 使用以下代码创建用于加密和解密 Cookie 数据的方法。

    private string EncryptedCookieData(string cookieValue,string cookieName)
    {
        string returnString = "";
        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, cookieName, DateTime.Now, DateTime.Now.AddMinutes(30), false, cookieValue, "/");
        returnString = FormsAuthentication.Encrypt(ticket);
        return returnString;
    }
    
    private string DecryptedCookieData(string cookie)
    {
        string returnString = "";
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie);
        returnString = ticket.UserData;
        return returnString;
    }
    
  8. 为 GetPostBackControlText 方法添加以下代码,该方法将返回一个字符串,该字符串指明是由 Logon 按钮单击事件触发回发、由 Logout 按钮事件触发回发还是二者都不触发回发。

    private string GetPostBackControlText()
    {
        Control control = null;
        Control c = null;
    
        foreach (string controlName in Page.Request.Form)
        {
            c = Page.FindControl(controlName);
            if (c is System.Web.UI.WebControls.Button)
            {
                control = c;
                break;
            }
        }
    
        if (control != null)
        {
            return ((Button)(control)).Text;
        }
        else
        {
            return "";
        }
    }
    
  9. 使用以下代码替代 GetXPathNavigator 方法,以根据是否返回结果来显示或隐藏登录/注销控件。

    protected override XPathNavigator GetXPathNavigator(string viewPath)
    {
        Results = base.GetXPathNavigator(viewPath);
    
        if (Results == null)
        /*
        Login failed, or no credentials were entered,
        so Login controls should be displayed.
        */
        {
            LogOutButton.Visible = false;
            LoginControlsRow.Visible = true;
        }
        else
        /*
        Login succeeded, so hide login controls,
        and show Logout button.
        */
        {
            LogOutButton.Visible = true;
            LoginControlsRow.Visible = false;
        }
        return Results;
    }
    

此演练中所述的示例 Web 部件不适用于被配置为使用表单身份验证的联合位置。若要支持基于 Cookie 的表单身份验证,您可能需要修改该示例。为此,请修改代码,使其执行以下操作:

  1. 检索为联合位置指定的 URL。

  2. 确定联合位置的表单登录页 URL。

  3. 请求联合位置的登录页。

  4. 在 UI 中显示登录表单,以便用户可以输入他们的凭据。

  5. 检索登录输入的元素和值(如果用户确认表单登录尝试成功)。

  6. 使用 FormsCredentials 对象,传递登录输入的元素和值以及登录页请求方法和指向联合位置的 URL,如以下代码段中所示。

    Creds = new FormsCredentials(credsLogonURL, credsLogonURL, credsLogonInput, credsSecureLogonInput, credsLogonMethod, cookies);
    

    备注

    只有当在 FormsCredentials(String, String, Dictionary<String, String>, Dictionary<String, SecureString>, String, CookieCollection) 构造函数的 Cookie 参数中传递的 CookieCollection 不包含任何 Cookie 对象时,此代码才将有用。

  7. 将表单登录输入元素随登录页请求方法和 URL 一起存储于加密的 Cookie 中,或使用单一登录 (SSO) 提供程序存储。

重要

如果在浏览器和服务器之间传递 Cookie 中的凭据,建议您使用 SSL 来保护客户端浏览器和 Web 服务器之间的通信。有关详细信息,请参阅步骤 3:部署自定义 Web 部件中的“安全注意事项”。

下一步骤

步骤 3:部署自定义 Web 部件

See Also

参考

FormsCredentials

FederatedResultsWebPart

FederatedResultsDatasource

FederatedResultsDatasourceView

SearchResultsBaseWebPart

SearchResultsBaseDatasource

SearchResultsBaseDatasourceView

概念

步骤 1:为自定义 Web 部件建立项目

创建一个带有凭据 UI 的自定义联合搜索 Web 部件

联合搜索概述