步骤 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 中的代码
在 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;
在以下代码行中将 WebControl 替换为 FederatedResultsWebPart 和 IPostBackEventHandler:
public class PerUserAuthWebPart : FederatedResultsWebPart, IPostBackEventHandler
在 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;
使用以下代码替代 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(); }
为 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); }
使用以下代码替代 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); }
使用以下代码创建用于加密和解密 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; }
为 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 ""; } }
使用以下代码替代 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; }
修改代码以支持基于 Cookie 的表单身份验证
此演练中所述的示例 Web 部件不适用于被配置为使用表单身份验证的联合位置。若要支持基于 Cookie 的表单身份验证,您可能需要修改该示例。为此,请修改代码,使其执行以下操作:
检索为联合位置指定的 URL。
确定联合位置的表单登录页 URL。
请求联合位置的登录页。
在 UI 中显示登录表单,以便用户可以输入他们的凭据。
检索登录输入的元素和值(如果用户确认表单登录尝试成功)。
使用 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 对象时,此代码才将有用。
将表单登录输入元素随登录页请求方法和 URL 一起存储于加密的 Cookie 中,或使用单一登录 (SSO) 提供程序存储。
重要
如果在浏览器和服务器之间传递 Cookie 中的凭据,建议您使用 SSL 来保护客户端浏览器和 Web 服务器之间的通信。有关详细信息,请参阅步骤 3:部署自定义 Web 部件中的“安全注意事项”。
下一步骤
See Also
参考
FederatedResultsDatasourceView
SearchResultsBaseDatasourceView