[源码下载]
化零为整WCF(17) - 安全(Security)
作者:webabcd
介绍
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
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);
}
}
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;
}
}
}
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("用户名或密码不正确");
}
}
}
}
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>
<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>
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();
}
}
}
}
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>
<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
[源码下载]