撰寫 SharePoint 2010 自訂表單登入頁面 (第二部分)
撰寫 SharePoint 2010 自訂表單登入頁面 (第二部分)
新增序言,並開放對網站擁有人提出意見
自從部落格網站升級到現在這個燎原惡火般的最新版本,整個格式設計就像 7 月 4 號的烤肉一樣令人厭惡,我總要想盡辦法屈就新版提供的可悲語法標記 (要不然就是根本沒提供應有的語法標記)。希望大家向網站管理員反映,讓他們知道這些令人頭昏眼花的頁面格式有多難閱讀!我會儘可能附上我寫的原始文章 Word 檔案。誰說科技始終來自於人性??好了,言歸正傳...
在第一部分 ( https://blogs.msdn.com/b/sharepoint_cht/archive/2010/11/23/writing-a-custom-forms-login-page-for-sharepoint-2010-part-1.aspx ) 中,已經說明了如何建立一份全新的表單登入頁面。 這個案例的背景因素,是要在現成使用者介面所提供的功能之外,另外設計所需功能,例如,雙重要素驗證。 這次我將逐步說明另一種不同案例。 在這次的範例中,現成使用者介面不必修改,但要在登入時加入一道步驟。
我會在這個特殊案例中要求所有使用者必須先同意接受網站使用條款,才能進入網站。 首先是建立新的登入頁面。 我會新增一個登入事件處理常式,查看使用者是否有個 Cookie 表示同意接受使用條款。 若沒有,就會將他們重新導向使用條款頁面,讓他們勾選同意選項,接著再將他們重新導回登入頁面。 當他們再次登入時,就可以看到他們有個使用條款 Cookie,這樣即可讓他們前往原本要求的資源位置,如首頁、文件等等。
第一步,先建立新的登入頁面, 也就是在專案中加入新的 ASPX 頁面。 在這個專案,我需要加入對 Microsoft.SharePoint 及 Microsoft.SharePoint.IdentityModel 的參考。 如需詳細了解如何加入對 Microsoft.SharePoint.IdentityModel 的參考,請參閱第一部分 (連結已列在前文)。 在登入頁面背後的程式碼,我還是用了一些 using 陳述式。 以下就是我所用的陳述式:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.IdentityModel.Pages;
using System.Diagnostics;
由於這是個使用表單型驗證的登入頁面,所以它需要繼承自 FormsSignInPage。 我的類別宣告如下所示:
public partial class AgreeLogin : FormsSignInPage
我第一件要做的事,是在程式碼中覆寫 Page Load 事件, 作法就是針對頁面的 ASP.NET Login 控制項,加入登入事件處理常式。 以下是 Page Load 事件的覆寫程式碼:
protected override void OnLoad(EventArgs e)
{
try
{
base.OnLoad(e);
this.signInControl.LoggingIn +=
new LoginCancelEventHandler(signInControl_LoggingIn);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
在此說明一下,之所以放入 try…catch 區段,是因為如果寫壞了頁面標記 (後面我會解釋),一旦呼叫基礎.OnLoad(e) 就會丟出錯誤。 接下來,則示範如何實作 LoggingIn 事件處理常式。
void signInControl_LoggingIn(object sender, LoginCancelEventArgs e)
{
//look for a cookie; if not there then redirect to our accept terms page
const string AGREE_COOKIE = "SignedTermsAgreement";
try
{
//we want to check for our cookie here
HttpCookieCollection cookies = HttpContext.Current.Request.Cookies;
HttpCookie ck = cookies[AGREE_COOKIE];
if (ck == null)
//user doesn't have the cookie indicating that they've signed the
//terms of use so redirect them
Response.Redirect("AgreeTerms.aspx?" +
Request.QueryString.ToString());
}
catch (Exception ex)
{
Debug.WriteLine("There was an error processing the request: " +
ex.Message);
}
}
所以,上面所作的動作就是,在使用者開始登入後,程式碼先要查看 Cookie,看看使用者是否表示同意接受網站使用條款。 如果找不到這個 Cookie,就將使用者重新導向到 AgreeTerms.aspx 頁面。 我加入的是登入頁面所收到的完整查詢字串,這樣才能知道使用者進入登入頁面時原本要求前往哪個資源,程式碼也才能知道等到他們同意使用條款後,要再將他們導引到哪個位置。 這其中有件很重要的事,就是變更 SharePoint Web 應用程式的 web.config,如此才能以匿名方式存取使用條款頁面, 否則,倘若使用條款頁面也像網站中其他每個頁面一樣,都受到安全保護,就會形成永無止境的迴圈 – 因為如果使用者沒有這個 Cookie,就會重新導向到 AgreeTerms.aspx 頁面,但基於這個頁面受到安全保護,又會將使用者重新導回自訂的登入頁面,一直這樣重複循環。 所以我將 web.config 改成在 </system.web> 結束標記之下加入一個項目:
<location path="_layouts/AgreeTerms.aspx">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
如此一來,使用者進入 AgreeTerms.aspx 頁面就不必經過驗證。 現在,我不打算深入這個頁面的使用者介面細節而讓人覺得麻煩,不過這個頁面背後的程式碼其實很簡單:
protected void SubmitBtn_Click(object sender, EventArgs e)
{
const string AGREE_COOKIE = "SignedTermsAgreement";
try
{
//make sure the checkbox is checked
if ((AgreeChk.Checked) && (Request.Browser.Cookies))
{
//create the cookie
HttpCookie ck = new HttpCookie(AGREE_COOKIE, "true");
//set the expiration so it's persisted
ck.Expires = DateTime.Now.AddYears(3);
//write it out
HttpContext.Current.Response.Cookies.Add(ck);
//get the src attribute from the query string and redirect there
Response.Redirect(Request.QueryString["Source"]);
}
else
StatusLbl.Text = "You must agree to the terms of use before continuing.";
}
catch (Exception ex)
{
string msg = "There was an error processing the request: " + ex.Message;
Debug.WriteLine(msg);
StatusLbl.Text = msg;
}
}
這應該很容易懂 – 如果使用者勾選方塊,程式碼就會產生 Cookie,接著再透過 Response.Redirect 將使用者導向原本要求的來源。 這個動作會將使用者帶回到登入頁面,這次當使用者進行登入時,系統會查看 Cookie,然後接下來的動作就易如反掌。 如果使用者不同意接受使用條款,當然就無法進入任何位置。
以上是就編寫程式角度說明整個邏輯,不過還有一件非常重要的事,就是登入頁面的標記語法。 前面提到,如果標記語法寫錯,麻煩可就大了。 所以首先,第一個要處理的是這個標記:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AgreeLogin.aspx.cs" Inherits="FormsLoginPage.AgreeLogin,FormsLoginPage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf32e76ff986e00f" MasterPageFile="~/_layouts/simple.master" %>
<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
<SharePoint:EncodedLiteral ID="ClaimsFormsPageTitle" runat="server" text="<%$Resources:wss,login_pagetitle%>" EncodeMethod='HtmlEncode'/>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
<SharePoint:EncodedLiteral ID="ClaimsFormsPageTitleInTitleArea" runat="server" text="<%$Resources:wss,login_pagetitle%>" EncodeMethod='HtmlEncode'/>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderSiteName" runat="server"/>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" ID="ClaimsFormsPageMessage" />
<asp:login id="signInControl" FailureText="<%$Resources:wss,login_pageFailureText%>" runat="server" width="100%">
<layouttemplate>
<asp:label id="FailureText" class="ms-error" runat="server"/>
<table class="ms-input">
<colgroup>
<col width="25%"/>
<col width="75%"/>
</colgroup>
<tr>
<td nowrap="nowrap"><SharePoint:EncodedLiteral ID="EncodedLiteral3" runat="server" text="<%$Resources:wss,login_pageUserName%>" EncodeMethod='HtmlEncode'/></td>
<td><asp:textbox id="UserName" autocomplete="off" runat="server" class="ms-long ms-login-textbox"/></td>
</tr>
<tr>
<td nowrap="nowrap"><SharePoint:EncodedLiteral ID="EncodedLiteral4" runat="server" text="<%$Resources:wss,login_pagePassword%>" EncodeMethod='HtmlEncode'/></td>
<td><asp:textbox id="password" TextMode="Password" autocomplete="off" runat="server" class="ms-long ms-login-textbox"/></td>
</tr>
<tr>
<td colspan="2" align="right"><asp:button id="login" commandname="Login" text="<%$Resources:wss,login_pagetitle%>" runat="server" /></td>
</tr>
<tr>
<td colspan="2"><asp:CheckBox id="RememberMe" text="<%$SPHtmlEncodedResources:wss,login_pageRememberMe%>" runat="server" /></td>
</tr>
</table>
</layouttemplate>
</asp:login>
</asp:Content>
唯一真正要更改的地方是第一行的 @Page 指令。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AgreeLogin.aspx.cs" Inherits="FormsLoginPage.AgreeLogin,FormsLoginPage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf32e76ff986e00f" MasterPageFile="~/_layouts/simple.master" %>
以黃色強調顯示的部分應該改為您自訂組件的強式名稱。
完成更改並經過編譯後,將這個組件登錄到全域組件快取 GAC 中,並將 ASPX 頁面複製到版面配置目錄。 在實際試驗執行之前,最後一個步驟是對使用表單型驗證的 Web 應用程式區域變更登入頁面。 現在移至管理中心,選取 [應用程式管理],按一下 [管理 Web 應用程式]。 選取您的 Web 應用程式,按一下工具列上的 [驗證提供者] 按鈕。 按一下要變更的區域,在出現的對話方塊中按一下 [自訂登入頁面] 選項按鈕,然後輸入登入頁面的 URL, 以本文範例來說,就是 _layouts/AgreeLogin.aspx。 儲存變更,然後就可以登入網站。
這就是登入我的網站後的所看到的自訂頁面。希望本文能幫助您順利獲得相同結果!
1. 一開始的登入頁面
2. 使用條款頁面
3. 最後進入網站
這是翻譯後的部落格文章。英文原文請參閱 Writing A Custom Forms Login Page for SharePoint 2010 Part 2