ASP.NET 服务器控件授权
原文:http://msdn.microsoft.com/zh-cn/library/aa479017.aspx
摘要:了解 ASP.NET 服務器控件的授權要求,了解可用于 .NET 框架版本 1.0 和 1.1 的 ASP.NET 控件授權實現。該實現可以進行擴展,以創建自定義的服務器端授權方案。(23 頁打印頁)
Nikhil Kothari Microsoft ASP.NET 小組
Vandana Datye 自由撰稿人
2003 年 7 月
適用于: Microsoft? ASP.NET
請下載 ASPNETControlLicensing.msi。
背景
本文假設讀者熟悉 Microsoft? ASP.NET 編程和 ASP.NET 服務器控件創建。
Developing Microsoft ASP.NET Server Controls and Components (ISBN 0-7356-1582-9)(Microsoft Press,2002。保留所有權利。)一書中,關于授權部分內容更詳細的闡述。該部分內容的使用已獲得 Microsoft Press 的許可。有關該書的詳細信息,請參閱 http://www.microsoft.com/mspress/books/5728.asp。
本頁內容
| 簡介 | |
| ASP.NET 服務器控件授權要求 | |
| 已授權控件演練 | |
| .NET 框架授權結構 | |
| ASP.NET 服務器控件授權基礎結構 | |
| 擴展默認授權方案 | |
| 過期許可方案 | |
| 加密的許可方案 | |
| 授權實現核對清單 | |
| 小結 |
簡介
Microsoft .NET 框架有一個內置的可擴展授權結構,支持所有托管組件(包括業務對象、Windows 窗體控件和 ASP.NET 服務器控件)的設計時授權和運行時授權。本文就建立在該結構的基礎上,以提供專門針對 ASP.NET 控件進行優化的授權實現,并且,您可以擴展該授權實現,以創建自定義授權方案,例如:
| ? | 簡單授權方案 - 只檢查是否存在有效的許可數據,以決定是否啟用控件。 |
| ? | 按每次使用授權方案 - 經過某個使用計數后,許可過期。此方案可用于控件的演示版。許可過期后,應用程序開發人員可以注冊(和購買)您的控件,然后收到一個不過期許可。 |
| ? | 只有當請求來自特定客戶機(如本地計算機)時,才在某個頁面中啟用 ASP.NET 服務器控件的授權方案。此方案可用于實現控件的試用版。 |
| ? | 依靠加密來防止應用程序開發人員進行許可數據欺騙的授權方案。 |
ASP.NET 服務器控件授權要求
ASP.NET 服務器控件授權方案必須滿足以下要求:
| ? | 支持不編譯方案。ASP.NET Web 應用程序常常使用動態編譯模型,因此沒有與應用程序相關聯的預編譯程序集。授權機制不應該依靠在應用程序的程序集中找到作為程序集資源嵌入的許可。 |
| ? | 支持運行時授權。頁面開發人員使用可視設計時工具及簡單文本編輯器,來開發自己的頁面。授權機制不能依靠設計時檢查,必須提供運行時驗證。而且,運行時授權實現不應與任何(可選的)設計時授權實現有依賴關系。 |
| ? | 支持許可緩存機制。理想情況下,每個應用程序只應該檢索一次許可數據,而不是針對每個頁面請求都進行檢索,因為檢索邏輯會涉及開銷較大的操作,例如,打開文件和對信息解密。應該在第一次需要許可時創建許可,并進行緩存,以便以后在服務器上重用。您仍然可以在每次使用許可來實現基于使用的授權方案時,驗證緩存的許可。 |
| ? | 支持 XCOPY 部署。ASP.NET 使得頁面開發人員能夠只是通過在網絡上的計算機之間復制文件,就可以部署其 Web 應用程序。授權方案不應該依靠注冊表,或者其他禁止簡單 XCOPY 部署的特定于計算機的資源。 |
為簡單起見,我們在前面的列表中使用了服務器控件這個術語。不過,授權要求適用于所有 ASP.NET 服務器組件。同樣,本文中描述的 ASP.NET 控件授權方案也適用于其他 ASP.NET 服務器組件。
返回頁首已授權控件演練
控件授權涉及三個關鍵元素:
| ? | 控件中支持授權的代碼 |
| ? | 許可數據 |
| ? | 檢查許可數據、發放許可以及在后來使用控件時驗證許可的類 |
已授權服務器控件
下面列出的 LicensedLabel 服務器控件是從 ASP.NET System.Web.UI.WebControls.Label 控件派生的,并為其添加了授權支持。以粗體顯示的代碼提供了授權功能。
// LicensedLabel.cs // using System; using System.ComponentModel; using System.Web.UI.WebControls; namespace LicensedControls { [LicenseProvider(typeof(ServerLicenseProvider))] public class LicensedLabel : Label { public LicensedLabel() { LicenseManager.Validate(typeof(LicensedLabel)); } } }該示例說明了為支持授權,您必須向任何服務器組件的代碼添加下列內容:
| ? | 在控件的構造函數中,調用 System.ComponentModel.LicenseManager 類的靜態方法 Validate,并將它作為參數傳遞到組件的類型中。如果該控件沒有有效許可,LicenseManager 的 Validate 方法將引發 System.ComponentModel.LicenseException。另一種方法是,在構造函數中,您可以調用 LicenseManager 類的靜態方法 IsValid,這樣就不會引發異常。如果您希望在沒有有效許可的情況下啟用控件(在簡裝版本上就是如此),請調用 IsValid 方法。 |
| ? | 將 System.ComponentModel.LicenseProviderAttribute 元數據屬性應用于您的組件,并向它傳遞執行組件授權的許可提供程序(從 System.ComponentModel.LicenseProvider 派生的類)的類型。本文中 ASP.NET 服務器控件授權基礎結構一節顯示了 LicensedLabel 控件的許可提供程序 ServerLicenseProvider 的實現。 |
如圖 1 所示,您為支持授權而必須對控件所做的更改是最小的。真正的授權功能在許可提供程序類中,稍后再說明這部分內容。
如果您已經在 Windows 窗體控件中實現了授權,您可能很驚奇地發現,LicensedLabel 不處置其許可。這是因為,LicensedLabel 使用一個在服務器上緩存許可的許可提供程序。
許可數據
許可數據提供由授權結構進行驗證并合并到許可中的信息。您可以用許多不同的方式提供許可數據(如過期日期、使用計數或唯一密鑰)。許可數據的類型和位置由特定的授權方案來指定。通常在擴展名為 .lic 的文件中提供許可數據。圖 1 中的 LicensedLabel 控件的許可數據位于一個名為 LicensedControls.LicensedLabel.lic 的文件中,該文件只包含文本 "LicensedControls.LicensedLabel is licensed"。
在頁面上使用已授權控件
隨本文的代碼示例提供的 ReadMe 文檔描述了如何構建這些示例。
在頁面中使用 LicensedLabel 控件
1.將 LicensedControls 程序集(包含 LicensedLabel 控件)復制到應用程序的 \Bin 目錄。如果您使用的是 Microsoft Visual Studio? .NET 并在您的 Web 應用程序項目中添加了對 LicensedControls 項目的引用,則不需要此步驟。
2.將 LicensedControls.LicensedLabel.lic 文件復制到應用程序的 Licenses\LicensedControls\1.0.0.0 目錄。
現在,您應該能從應用程序中的任何頁面使用控件。
下面的代碼顯示了一個使用 LicensedLabel 控件的頁面。
<%@ Page language="c#" %> <%@ Register TagPrefix="lc" Assembly="LicensedControls" Namespace="LicensedControls" %> <html> <head> <title>LicensedLabel Sample</title> </head> <body> <form method="post" runat="server" ID="Form1"> <p> <lc:LicensedLabel runat="server" id="LicensedLabel1" Text="Hello World!" /> </p> </form> </body> </html>要查看授權是否正在生效,請刪除 LicensedControls.LicensedLabel.lic 文件或將它移到另一個位置。重新生成應用程序或做出某個可導致應用程序重新啟動的更改。此步驟的作用是清除由 ServerLicenseProvider(LicensedLabel 控件的元數據中指定的許可提供程序)管理的許可緩存。在瀏覽器中請求 LicensedLabelTest.aspx 頁。該頁將生成下圖中顯示的錯誤。
圖 1. LicensedLabelTest.aspx 頁嘗試在沒有有效許可的情況下使用 LicensedLabel 時生成的錯誤
返回頁首.NET 框架授權結構
下圖(圖 2)說明了 .NET 框架的授權結構。從中可以看出當一個頁面嘗試對前面一節描述的 LicensedLabel 控件進行實例化時發生的主要步驟。雖然實際步驟發生在服務器控件的上下文中,但該圖顯示了構成 .NET 框架授權結構的類,以及任何運行時授權方案所共有的關鍵步驟。許可提供程序執行的確切步驟是特定于提供程序實現的具體授權方案的。例如,正如本文中 ASP.NET 服務器控件授權基礎結構一節所描述的,圖中顯示的許可緩存功能就是特定于 ServerLicenseProvider 的。以粗體顯示的類是 .NET 框架類,以斜體顯示的類是實現的派生類。
圖 2. .NET 框架的授權結構
對控件實施授權的主要步驟包括:
1.已授權控件在其構造函數中調用靜態方法 System.ComponentModel.LicenseManager.Validate。(該控件也可以在其構造函數中調用靜態方法 LicenseManager.IsValid。在這種情況下,返回類型與圖中顯示的會有所不同,并且不會引發異常。)
2.LicenseManager.Validate 方法檢查組件的元數據,從應用于該組件的 LicenseProviderAttribute 屬性獲得許可提供程序的類型。許可提供程序類必須從 System.ComponentModel.LicenseProvider 類派生。
3.LicenseManager 對許可提供程序類(System.ComponentModel.LicenseProviderAttribute 元數據屬性中指定了它的類型)進行實例化,將該組件的類型傳遞到該許可提供程序,并指出該組件在設計時使用還是在運行時使用。
4.許可提供程序在許可緩存中查找組件的許可。如果找到一個許可,許可提供程序就驗證該許可。注意,許可緩存查找和許可存儲不是一般的要求,而是特定于 ServerLicenseProvider - 我們已經實現的許可提供程序的。
a.(僅限第一次)許可提供程序獲取許可數據,并進行驗證。如果該數據無效,許可提供程序將引發 System.ComponentModel.LicenseException 異常。
b.(僅限第一次)如果許可數據有效,許可提供程序將創建一個許可(從 System.ComponentModel.License 派生的類)。此外,許可提供程序還會驗證許可,如果許可有效,則將它存儲在許可緩存中。
5.許可提供程序將一個有效許可返回許可管理器,或引發許可異常。
6.LicenseManager.Validate 方法返回一個有效許可,或將許可異常傳遞到調用代碼中。
7.如果 LicenseManager 返回有效許可,構造函數將對該類進行初始化,該控件將被實例化。否則,構造函數將 LicenseException 異常傳遞到試圖實例化該控件的代碼。本文已授權控件演練一節中的圖所顯示的錯誤消息是 ASP.NET 運行時產生的,ASP.NET 運行時處理當某頁在沒有有效許可的情況下使用已授權控件時,由該控件的構造函數傳遞的許可異常。
初次創建指的是組件在 Web 應用程序中的第一次實例化。如果在同一頁上或者在應用程序中的另一頁上創建了該組件的另一個實例(在同一個請求中或者在后來的請求中),則不會發生步驟 4a 和 4b。出于性能方面的原因,ServerLicenseProvider 按每個應用程序對許可進行緩存(而不是按每頁或每個會話)。
.NET 框架中授權結構的設計使得非法使用組件非常困難(但并非不可能)。如果用戶試圖在沒有許可的情況下使用一個已授權組件,授權機制就會使用戶很明顯地看出該組件正在被非法使用。授權不產生組件篡改證據。
.NET 框架中的授權結構是由 System.ComponentModel 命名空間中的以下四個類提供的:
| ? | LicenseManager:該類負責對組件的元數據中指定的許可提供程序進行實例化。許可管理器還向許可提供程序傳遞組件的類型和授權上下文,授權上下文指明該組件是在設計時使用還是在運行時使用。除了在組件的構造函數中調用 LicenseManager 類的 Validate 或 IsValid 方法之外,您無需知道有關 LicenseManager 的其他詳細信息。 |
| ? | LicenseProviderAttribute:此屬性指定負責創建和驗證組件許可的許可提供程序的類型。您必須將此屬性應用于支持授權的組件。 |
| ? | LicenseProvider:該類包含任何授權方案的核心功能 - 即發放和驗證許可的任務。要實現授權支持,您必須通過從 LicenseProvider 派生來創建自定義許可提供程序,并實現基類的抽象方法 GetLicense,以提供授權邏輯。許可提供程序 ServerLicenseProvider 的實現將在本文下一節討論。 |
| ? | License:該類是許可數據(如包含在 .lic 文件中的許可數據)的軟件抽象。要實現許可類,您必須從 License 類派生,并實現基類的抽象屬性 LicenseKey。在本文的下一節,我們將實現一個與 ServerLicenseProvider 一起使用的許可類。 |
.NET 框架在 System.ComponentModel.LicFileLicenseProvider 類中提供了許可提供程序的默認實現。該許可提供程序依靠可視設計器(如 Visual Studio .NET)在設計時和編譯期間獲取授權數據,將許可數據作為資源嵌入使用已授權組件的應用程序的程序集中。LicFileLicenseProvider 類可以由 Windows 窗體控件使用,但它不滿足本文中 ASP.NET 服務器控件授權要求一節描述的 ASP.NET 服務器控件授權要求。
返回頁首ASP.NET 服務器控件授權基礎結構
本節將描述核心授權實現,它提供了 ASP.NET 服務器控件授權方案的具體部署。該實現包含在兩個類中,ServerLicenseProvider 和 ServerLicense,它們分別從 LicenseProvider 和 License 類中派生。我們希望在將來的 ASP.NET 版本中,類似的一組基類中有內置的授權支持。您可以使用和擴展 ServerLicenseProvider 和 ServerLicense 類,而不必檢查其源代碼,就像您使用 .NET 框架中的類一樣。不過,為完整起見,本節包括了這些類的代碼。
ServerLicenseProvider 類
ServerLicenseProvider 類是從 LicenseProvider 派生的,它覆蓋了 GetLicense 方法,以實現核心服務器控件授權要求。ServerLicenseProvider 滿足了本文前面說明的服務器授權要求 - 不編譯模型、運行時授權支持、授權緩存和 XCOPY 部署。ServerLicenseProvider 將實現從 .lic 文本文件加載許可數據的默認授權方案,該文件存儲在 Web 應用程序根目錄中一個名為 Licenses 的目錄中。此目錄下的結構基于本文已授權控件演練一節中顯示的程序集的名稱和版本。該默認方案依靠在 .lic 文件中找到以下內容:“<組件的完整類型名> is licensed.”。
// ServerLicenseProvider.cs // using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.IO; using System.Diagnostics; using System.Globalization; using System.Web; namespace LicensedControls { public class ServerLicenseProvider : LicenseProvider { private static readonly ServerLicenseCollector LicenseCollector = new ServerLicenseCollector(); protected virtual ServerLicense CreateLicense(Type type, string key) { return new ServerLicense(type, key); } protected virtual ServerLicense CreateEmptyLicense(Type type) { return new ServerLicense(type, String.Empty); } public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions) { ServerLicense license = null; string errorMessage = null; if (context.UsageMode == LicenseUsageMode.Designtime) { license = CreateEmptyLicense(type); } else { license = LicenseCollector.GetLicense(type); if (license == null) { string licenseData = GetLicenseData(type); if ((licenseData != null) && (licenseData.Length != 0)) { if (ValidateLicenseData(type, licenseData)) { ServerLicense newLicense = CreateLicense(type, licenseData); if (ValidateLicense(newLicense, out errorMessage)) { license = newLicense; LicenseCollector.AddLicense(type, license); } } } } else { if (ValidateLicense(license, out errorMessage) == false) { license = null; } } } if (allowExceptions && (license == null)) { if (errorMessage == null) { throw new LicenseException(type); } else { throw new LicenseException(type, instance, errorMessage); } } return license; } protected virtual string GetLicenseData(Type type) { string licenseData = null; Stream licenseStream = null; try { licenseStream = GetLicenseDataStream(type); if (licenseStream != null) { StreamReader sr = new StreamReader(licenseStream); licenseData = sr.ReadLine(); } } finally { if (licenseStream != null) { licenseStream.Close(); licenseStream = null; } } return licenseData; } protected virtual Stream GetLicenseDataStream(Type type) { string assemblyPart = type.Assembly.GetName().Name; string versionPart = type.Assembly.GetName().Version.ToString(); string relativePath = "~/licenses/" + assemblyPart + "/" + versionPart + "/" + type.FullName + ".lic"; string licensesFile = null; try { licensesFile = HttpContext.Current.Server.MapPath(relativePath); if (File.Exists(licensesFile) == false) { licensesFile = null; } } catch { } if (licensesFile != null) { return new FileStream(licensesFile, FileMode.Open, FileAccess.Read, FileShare.Read); } return null; } protected virtual bool ValidateLicense(ServerLicense license, out string errorMessage) { errorMessage = null; return true; } protected virtual bool ValidateLicenseData(Type type, string licenseData) { string licenseKey = type.FullName + " is licensed."; return String.Compare(licenseKey, licenseData, true, CultureInfo.InvariantCulture) == 0; } private sealed class ServerLicenseCollector { private IDictionary _collectedLicenses; public ServerLicenseCollector() { _collectedLicenses = new HybridDictionary(); } public void AddLicense(Type objectType, ServerLicense license) { if (objectType == null) { throw new ArgumentNullException("objectType"); } if (license == null) { throw new ArgumentNullException("objectType"); } _collectedLicenses[objectType] = license; } public ServerLicense GetLicense(Type objectType) { if (objectType == null) { throw new ArgumentNullException("objectType"); } if (_collectedLicenses.Count == 0) { return null; } return (ServerLicense)_collectedLicenses[objectType]; } public void RemoveLicense(Type objectType) { if (objectType == null) { throw new ArgumentNullException("objectType"); } _collectedLicenses.Remove(objectType); } } } }ServerLicense 類
ServerLicense 是與 ServerLicenseProvider 兼容的基許可類。ServerLicense 是從 License 類派生的,并實現了該基類的抽象屬性 LicenseKey。
// ServerLicense.cs // using System; using System.ComponentModel; using System.Diagnostics; namespace LicensedControls { public class ServerLicense : License { private Type _type; private string _key; public ServerLicense(Type type, string key) { _type = type; _key = key; } public override string LicenseKey { get { return _key; } } public Type LicensedType { get { return _type; } } public override void Dispose() { } } } 返回頁首擴展默認授權方案
ServerLicenseProvider 和 ServerLicense 類實現了一個簡單的默認授權方案。您可以對這些類進行擴展,以實現您自己的具有更復雜驗證邏輯的自定義授權方案。要實現自定義授權方案,請從 ServerLicenseProvider 派生您自己的許可提供程序,并覆蓋它的一個或多個虛擬方法(見下表)。要完成您的授權方案的實現,您可能還必須實現從 ServerLicense 派生的并與許可提供程序一起使用的許可類。
下表描述了 ServerLicenseProvider 類中定義的可覆蓋方法。
| 可覆蓋的 | 說明 |
| protected virtual ServerLicense CreateLicense(Type type, string key) | 創建并返回指定的已授權類型的 ServerLicense 實例。該字符串參數是與類型相關聯的已驗證許可數據。派生的許可提供程序可以覆蓋此方法,以返回派生的許可。例如,請參見本文中過期許可方案一節中描述的 ExpiringLicenseProvider。 |
| protected virtual ServerLicense CreateEmptyLicense(Type type) | 創建并返回不與真正的許可數據相關聯的空的 ServerLicense 實例。ServerLicenseProvider 使用空的許可來支持設計時使用。派生的許可提供程序可以覆蓋此方法,以返回空的派生許可。 |
| protected virtual string GetLicenseData(Type type) | 通過讀取許可流中的第一行數據,從許可流檢索許可數據。派生的許可提供程序可以覆蓋此方法,以便從不基于流的其他許可存儲中進行讀取。 |
| protected virtual Stream GetLicenseDataStream(Type type) | 打開用于讀取許可數據的流。此方法還包含形成到適當的 .lic 文件的虛擬路徑的邏輯。派生的許可提供程序可以覆蓋此方法,以返回本文中加密的許可方案一節 EncryptedLicenseProvider 示例中顯示的自定義流實現。 |
| protected virtual bool ValidateLicense(ServerLicense license, out string errorMessage) | 驗證緩存的許可。該驗證在每次請求許可時發生。派生的許可提供程序可以覆蓋此方法,以實現自己的驗證邏輯。本文過期許可方案一節中描述的 ExpiringLicenseProvider 示例實現了一個基于使用的授權方案。 |
| protected virtual bool ValidateLicenseData(Type type, string licenseData) | 驗證許可數據,如果數據有效則創建許可并返回 True。派生的許可提供程序可通過覆蓋此方法實現自定義驗證規則。 |
過期許可方案
本節和下一節將說明如何擴展提供的默認授權實現,以創建自定義授權方案。本節方案中顯示的過期許可方案將通過在控件已使用指定次數后禁用該控件來擴展默認方案。此方案可用于控件的演示版。
過期許可方案在 ExpiringLicenseProvider 類中實現。
// ExpiringLicenseProvider.cs // using System; using System.Diagnostics; using System.Globalization; namespace LicensedControls { public class ExpiringLicenseProvider : ServerLicenseProvider { protected override ServerLicense CreateLicense(Type type, string key) { string[] parts = key.Split(';'); Debug.Assert(parts.Length == 2); return new ExpiringLicense(type, key, Int32.Parse(parts[1], CultureInfo.InvariantCulture)); } protected override bool ValidateLicense(ServerLicense license, out string errorMessage) { errorMessage = null; ExpiringLicense testLicense = (ExpiringLicense)license; testLicense.IncrementUsageCounter(); if (testLicense.IsExpired) { errorMessage = "The License for " + testLicense.LicensedType.Name + " has expired."; return false; } return true; } protected override bool ValidateLicenseData(Type type, string licenseData) { string[] parts = licenseData.Split(';'); if (parts.Length == 2) { return base.ValidateLicenseData(type, parts[0]); } else { return false; } } } }ExpiringLicenseProvider 類是從 ServerLicenseProvider 派生的,它覆蓋了該基類的下列方法:
| ? | CreateEmptyLicense,以創建不與真正的許可數據相關聯的許可。空的許可適于在設計時使用。 |
| ? | CreateLicense,以創建具有 .lic 文件指定的使用計數的許可。 |
| ? | ValidateLicense,以檢查許可是否已過期。ValidateLicense 在進行檢查之前將遞增使用計數。 |
| ? | ValidateLicenseData,以檢查 .lic 文件中的文本字符串是否包含用分號分隔的兩部分:“<完整類型名> is licensed.;<使用計數>”。 |
ExpiringLicenseProvider 類使用 ExpiringLicense 類作為其許可類型。
// ExpiringLicense.cs // using System; namespace LicensedControls { public class ExpiringLicense : ServerLicense { private int _usageLimit; private int _usageCount; public ExpiringLicense(Type type, string key, int usageLimit) : base(type, key) { _usageLimit = usageLimit; } public bool IsExpired { get { return _usageCount > _usageLimit; } } public void IncrementUsageCounter() { _usageCount++; } } }ExpiringLicense 類實現了以下邏輯:
| ? | 定義兩個私有字段,以存儲使用限制和使用計數。 |
| ? | 公開 IncrementUsageCounter 方法,該方法在每次訪問 ExpiringLicense 對象時會遞增使用計數。 |
| ? | 公開 IsExpired 屬性,該屬性通過將使用計數與使用限制進行比較,確定許可是否已經過期。 |
注意,使用計數可保存在 ExpiringLicense 本身中,因為服務器許可是進行緩存的。ServerLicenseProvider 對于一個特定的組件類型只會創建許可的一個實例,并將許可保存在其內部許可緩存中。不過,在緩存的許可對象中存儲數據并不十分安全。例如,如果應用程序重新啟動,使用計數將重置為零,因此使得總使用限制大于許可數據中指定的許可限制。同樣,如果應用程序是在服務器場上部署的,所有服務器上的總使用計數則可能會超過預期的使用限制。不過,ExpiringLicense 中實現的邏輯還是可以接受的,因為授權的目的是為了在應用程序的性能不會受到重大影響的情況下,防止對組件進行未經授權的使用。
下面的代碼顯示了將 ExpiringLicenseProvider 類用于其授權方案的控件。
// ExpiringLicensedLabel.cs // using System; using System.ComponentModel; using System.Web.UI.WebControls; namespace LicensedControls { [ LicenseProvider(typeof(ExpiringLicenseProvider)) ] public class ExpiringLicensedLabel : Label { public ExpiringLicensedLabel() { LicenseManager.Validate(typeof(ExpiringLicensedLabel)); } } }ExpiringLicensedLabel 的.lic 文件中的內容是 "LicensedControls.ExpiringLicensedLabel is licensed.;5"。
如上面的示例所示,如果許可是在一個明文文件中指定的,應用程序開發人員則可以很容易地欺騙該文件中的數據。您可以通過對許可數據加密(如下節所示)來提高授權方案的安全性。
返回頁首加密的許可方案
本節將擴展默認方案,以實現一個加密的許可提供程序,它會在讀取加密的許可數據時對這些加密許可數據進行解密。下面的代碼顯示了 EncryptedLicenseProvider 類。
// EncryptedLicenseProvider.cs // using System; using System.Diagnostics; using System.IO; using System.Security.Cryptography; namespace LicensedControls { public class EncryptedLicenseProvider : ServerLicenseProvider { // This is a 64-bit key generated from the string // "5FB281F6". // private static readonly byte[] encryptionKeyBytes = new byte[] { 0x35, 0x46, 0x42, 0x32, 0x38, 0x31, 0x46, 0x36 }; protected override Stream GetLicenseDataStream(Type type) { Stream baseStream = base.GetLicenseDataStream(type); if (baseStream == null) { return null; } DESCryptoServiceProvider des = new DESCryptoServiceProvider(); des.Key = encryptionKeyBytes; des.IV = encryptionKeyBytes; ICryptoTransform desDecryptor = des.CreateDecryptor(); return new CryptoStream(baseStream, desDecryptor, CryptoStreamMode.Read); } } }EncryptedLicenseProvider 類是從 ServerLicenseProvider 派生的,并覆蓋了 GetLicenseDataStream 方法,該方法創建 System.IO.Stream 對象來讀取許可數據。EncryptedLicenseProvider 用 System.Security.Cryptography.CryptoStream 來包裝此流,以便在許可數據被讀入時對其進行解密。示例中使用的 CryptoStream 采用了帶有 64 位加密密鑰的數據加密標準 (DES) 密碼算法,加密密鑰作為私有字段 encryptionKeyBytes 嵌入到了 EncryptedLicenseProvider 類本身。之所以允許用這種方式嵌入密鑰,是因為授權結構的設計使得破壞許可非常困難,甚至不可能。Win32 安全 API 提供了更多存儲加密密鑰的更加復雜的機制;不過,那些技術通常不適于 XCOPY 部署。
ServerLicense 很適合作為 EncryptedLicenseProvider 的許可類,因此無需為此許可提供程序實現任何派生的許可類。
下面代碼中顯示的 EncryptedLicensedLabel 控件使用了在 EncryptedLicenseProvider 類中實現的授權方案。
// EncryptedLicensedLabel.cs // using System; using System.ComponentModel; using System.Web.UI.WebControls; namespace LicensedControls { [ LicenseProvider(typeof(EncryptedLicenseProvider)) ] public class EncryptedLicensedLabel : Label { public EncryptedLicensedLabel() { LicenseManager.Validate(typeof(EncryptedLicensedLabel)); } } }下圖顯示了與 EncryptedLicensedLabel 相關聯的 .lic 文件。
圖 3. LicensedControls.EncryptedLicensedLabel 文件的內容
此文件的內容是使用 EncryptedLicenseProvider 中嵌入的同一個密鑰加密的。該文件中的數據是字符串 "LicensedControls.EncryptedLicensedLabel is licensed"。不過,由于已經加密,人們無法閱讀。本文的示例文件中提供了加密工具 EncLicGen.exe 及其源代碼 EncryptedLicenseGenerator.cs。
加密使授權方案變得更加強大。例如,您可以在過期許可方案中使用加密來提高其安全性。您可以將自己的數據和用戶的注冊信息組合在一起進行加密,而不是像此示例中那樣對固定字符串加密。
返回頁首授權實現核對清單
下表描述了實現服務器控件的授權時需要執行的任務:
| ? | 實現一個從 ServerLicenseProvider 類派生的許可提供程序,并覆蓋基類的一個或多個虛擬方法,以提供授權方案的邏輯。 |
| ? | 實現一個從 ServerLicense 類派生、與您實現的許可提供程序一起使用的許可類(請參見上一個項目符號后的內容)。如果 ServerLicense 適用于您的授權方案,則不需要此步驟(如本文上一節的 EncryptedLicenseProvider 示例所示)。 |
| ? | 通過應用 LicenseProviderAttribute 元數據屬性并向它傳遞您實現的許可提供程序的類型(請參見第一個項目符號后的內容),向您的組件添加授權支持。還需要從組件的構造函數調用 LicenseManager.Validate。 |
| ? | 為組件創建授權數據。您可以將此數據保存在 .lic 文件中,或以授權方案所要求的任何其他形式保存。 |
| ? | (可選)創建一個工具來生成許可數據。例如,隨本文的示例文件提供的加密工具 EncLicGen.exe。如果您要創建用于商用分發的組件,您會發現擁有一個自動創建許可數據的工具非常有用。 |
| ? | 如果組件的許可數據包含在文件中(如 .lic 文件),請向應用程序開發人員提供有關說明,用于創建您的授權方案所需的目錄結構,以及將許可文件復制到 Web 應用程序中的必要位置。 |
小結
本文討論了 ASP.NET 服務器控件授權的要求和 .NET 框架授權結構。我們講述了三個關鍵要素 - 控件中支持授權的代碼、許可數據以及發放和驗證許可的許可提供程序類 - 是如何提供授權功能的。我們提供了一個默認的 ASP.NET 服務器控件授權實現,該實現創建了服務器控件授權方案的具體部署,我們還說明了如何通過創建不同的自定義授權方案來擴展此默認授權實現。
?
總結
以上是生活随笔為你收集整理的ASP.NET 服务器控件授权的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cocoa Touch国际化
- 下一篇: iqoo有耳机吗(iQOO手机)