介紹
WCF(Windows Communication Foundation) - 安全(Security):本文以用戶名和密碼做驗證,通過X.509證書做加密為例
示例
1、證書
setup.bat makecert -sr LocalMachine -ss My -a sha1 -n CN=Webabcd -sky exchange -pe certmgr -add -r LocalMachine -s My -c -n Webabcd -s TrustedPeople
2、服務
IHello.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCF.ServiceLib.Security { /**//// <summary> /// IHello接口 /// </summary> [ServiceContract] public interface IHello { /**//// <summary> /// 打招呼方法 /// </summary> /// <param name="name">人名</param> /// <returns></returns> [OperationContract] string SayHello(string name); } }
Hello.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCF.ServiceLib.Security { /**//// <summary> /// Hello類 /// </summary> public class Hello : IHello { /**//// <summary> /// 打招呼方法 /// </summary> /// <param name="name">人名</param> /// <returns></returns> public string SayHello(string name) { return "Hello: " + name; } } }
CustomNamePasswordValidator.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCF.ServiceLib.Security { /**//// <summary> /// 自定義的用戶名/密碼驗證類 /// </summary> public class CustomNamePasswordValidator : System.IdentityModel.Selectors.UserNamePasswordValidator { /**//// <summary> /// 驗證指定的用戶名和密碼 /// </summary> /// <param name="userName">要驗證的用戶名</param> /// <param name="password">要驗證的密碼</param> public override void Validate(string userName, string password) { if (!(userName == "webabcd" && password == "webabcd")) { throw new FaultException("用戶名或密碼不正確"); } } } }
3、宿主
Hello.svc
<%@ ServiceHost Language="C#" Debug="true" Service="WCF.ServiceLib.Security.Hello" %>
Web.config
<?xml version="1.0"?> <configuration> <system.serviceModel> <services> <!--name - 提供服務的類名--> <!--behaviorConfiguration - 指定相關的行為配置--> <service name="WCF.ServiceLib.Security.Hello" behaviorConfiguration="SecurityBehavior"> <!--address - 服務地址--> <!--binding - 通信方式--> <!--contract - 服務契約--> <endpoint address="" binding="wsHttpBinding" contract="WCF.ServiceLib.Security.IHello" bindingConfiguration="SecurityBindingConfiguration" /> </service> </services> <behaviors> <serviceBehaviors> <behavior name="SecurityBehavior"> <!--httpGetEnabled - 指示是否發布服務元數據以便使用 HTTP/GET 請求進行檢索,如果發布 WSDL,則為 true,否則為 false,默認值為 false--> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true"/> <serviceCredentials> <!--userNamePasswordValidationMode - 以用戶名/密碼模式來進行驗證的方法--> <!--UserNamePasswordValidationMode.Windows - 用戶名映射到 Windows 用戶--> <!--UserNamePasswordValidationMode.MembershipProvider - 提供基於已配置的 MembershipProvider 的密碼驗證--> <!--UserNamePasswordValidationMode.Custom - 基於已配置的自定義 UsernamePasswordValidator 的自定義身份驗證--> <!--customUserNamePasswordValidatorType - 所使用的自定義用戶名密碼驗證程序的類型--> <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCF.ServiceLib.Security.CustomNamePasswordValidator, WCF.ServiceLib" /> <!--findValue - 指定要在 X.509 證書存儲區中搜索的值--> <!--storeLocation - 指定客戶端可用於驗證服務器證書的證書存儲區位置(LocalMachine - 分配給本地計算機的 X.509 證書存儲區;CurrentUser - 當前用戶使用的 X.509 證書存儲區)--> <!--storeName - 要打開的 X.509 證書存儲區的名稱(參看:StoreName枚舉。AddressBook, AuthRoot, CertificateAuthority, Disallowed, My, Root, TrustedPeople, TrustedPublisher)--> <!--x509FindType - 要執行的 X.509 搜索的類型(參看:X509FindType枚舉)--> <serviceCertificate findValue="Webabcd" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" /> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <bindings> <wsHttpBinding> <binding name="SecurityBindingConfiguration"> <security> <!--clientCredentialType - 客戶端用以進行身份驗證的憑據的類型,默認值 UserName --> <!--BasicHttpMessageCredentialType.UserName - 使用用戶名憑據對客戶端進行身份驗證--> <!--BasicHttpMessageCredentialType.Certificate - 使用證書對客戶端進行身份驗證--> <message clientCredentialType="UserName" /> </security> </binding> </wsHttpBinding> </bindings> </system.serviceModel> </configuration>
4、客戶端
Hello.aspx
<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Hello.aspx.cs" Inherits="Sample_Security" Title="安全(Security)" %> <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server"> <p> 以用戶名和密碼做驗證,通過X.509證書做加密為例 </p> <p> <asp:Label ID="lblMsg" runat="server" /> </p> <p> 用戶名:<asp:TextBox ID="txtUserName" runat="server" Text="webabcd" /> 密碼:<asp:TextBox ID="txtPassword" runat="server" Text="webabcd" /> </p> <p> <asp:TextBox ID="txtName" runat="server" Text="webabcd" /> <asp:Button ID="btnSayHello" runat="server" Text="Hello" OnClick="btnSayHello_Click" /> </p> </asp:Content>
Hello.aspx.cs
using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; public partial class Sample_Security : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnSayHello_Click(object sender, EventArgs e) { using (var proxy = new SecuritySvc.HelloClient()) { try { // proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.PeerTrust; proxy.ClientCredentials.UserName.UserName = txtUserName.Text; proxy.ClientCredentials.UserName.Password = txtPassword.Text; lblMsg.Text = proxy.SayHello(txtName.Text); } catch (TimeoutException ex) { lblMsg.Text = ex.ToString(); proxy.Abort(); } catch (Exception ex) { lblMsg.Text = ex.ToString(); proxy.Abort(); } } } }
Web.config
<?xml version="1.0"?> <configuration> <system.serviceModel> <client> <!--address - 服務地址--> <!--binding - 通信方式--> <!--contract - 服務契約--> <!--bindingConfiguration - 指定相關的綁定配置--> <!--behaviorConfiguration - 指定相關的行為配置--> <endpoint address="http://localhost:3502/ServiceHost/Security/Hello.svc" binding="wsHttpBinding" contract="SecuritySvc.IHello" bindingConfiguration="HelloBindingConfiguration" behaviorConfiguration="HelloBehaviorConfiguration"> <identity> <!--encodedValue - 此證書編碼的值。公鑰,用於加密用戶名和密碼。測試時,請根據實際情況修改此值--> <certificate encodedValue="AwAAAAEAAAAUAAAAwMJESjc9Bbgeh9hIrrdrlMz0nfEgAAAAAQAAALMBAAAwggGvMIIBXaADAgECAhBC+dqPonX5pEwDPMLbdE9MMAkGBSsOAwIdBQAwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3kwHhcNMDgwNzE1MDczODIwWhcNMzkxMjMxMjM1OTU5WjASMRAwDgYDVQQDEwdXZWJhYmNkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwfrBPcMSOWVJmDnn+EFfCOslH0OqC5s67C6e19XQ7oMh6a9hP9Os4hefNoGxcdPK3orV4y4pHn0VOvHgaeAJqreRjmgmyb+h2BDB7nkmhchBxQZUx4jSX0GUrqECZm9uUMrNq8vx7NtaEuEMs5q50KPaxrv6PwuKLssNnb3WC1wIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwCQYFKw4DAh0FAANBAE/6rAQhU3X1RficEHPEeUAX7HQQXZDYByQt0QqE7C8PaViQWlWU+Sp8u9Oy3ce4DSg3wgQLL/DIknG7FMIiGRE=" /> </identity> </endpoint> </client> <bindings> <wsHttpBinding> <binding name="HelloBindingConfiguration"> <security> <!--clientCredentialType - 客戶端用以進行身份驗證的憑據的類型,默認值 UserName --> <!--BasicHttpMessageCredentialType.UserName - 使用用戶名憑據對客戶端進行身份驗證--> <!--BasicHttpMessageCredentialType.Certificate - 使用證書對客戶端進行身份驗證--> <message clientCredentialType="UserName" /> </security> </binding> </wsHttpBinding> </bindings> <behaviors> <endpointBehaviors> <behavior name="HelloBehaviorConfiguration"> <clientCredentials> <serviceCertificate> <!--authentication - 證書驗證模式 --> <!--X509CertificateValidationMode.None - 不使用證書驗證--> <!--X509CertificateValidationMode.PeerTrust - 如果證書位於被信任的人的存儲區中,則有效--> <!--X509CertificateValidationMode.ChainTrust - 如果該鏈在受信任的根存儲區生成證書頒發機構,則證書有效--> <!--X509CertificateValidationMode.PeerOrChainTrust -如果證書位於被信任的人的存儲區或該鏈在受信任的根存儲區生成證書頒發機構,則證書有效 --> <!--X509CertificateValidationMode.Custom -用戶必須插入自定義 X509CertificateValidator 以驗證證書 --> <authentication certificateValidationMode="PeerTrust" /> </serviceCertificate> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel> </configuration>
運行結果:
單擊"btnSayHello"按鈕,顯示"Hello: webabcd"。經過加密的用戶名和密碼放在SOAP頭中傳輸。
OK