WCF支持多种认证技术,例如Windowns认证、X509证书、Issued Tokens、用户名密码认证等,在跨Windows域分布的系统中,用户名密码认证是比较常用的,要实现用户名密码认证,就必须需要X509证书,用来加密用户名和密码。
1. 创建数字证书
makecert -sr localmachine -ss My -n CN=ejiyuan -sky exchange -pe -r。
2. 创建服务代码
[ServiceContract]
public interface ICalculator
{
[OperationContract]
double add( double x, double y);
}
public class CalculatorService : ICalculator
{
public double add( double x, double y)
{
return x + y;
}
}
class Program
{
static void Main( string [] args)
{
ServiceHost _serviceHost = new ServiceHost( typeof (CalculatorService));
_serviceHost.Opened += (s, q) =>
{
Console.WriteLine( " 服务已启动 " );
Console.Read();
};
_serviceHost.Open();
}
}
public interface ICalculator
{
[OperationContract]
double add( double x, double y);
}
public class CalculatorService : ICalculator
{
public double add( double x, double y)
{
return x + y;
}
}
class Program
{
static void Main( string [] args)
{
ServiceHost _serviceHost = new ServiceHost( typeof (CalculatorService));
_serviceHost.Opened += (s, q) =>
{
Console.WriteLine( " 服务已启动 " );
Console.Read();
};
_serviceHost.Open();
}
}
3. 设置安全验证模式
<
bindings
>
< netTcpBinding >
< binding name ="nonSessionBinding" >
<!-- 当前绑定的安全认证模式 -->
< security mode ="Message" >
<!-- 定义消息级安全性要求的类型,为证书 -->
< message clientCredentialType ="UserName" />
</ security >
</ binding >
</ netTcpBinding >
</ bindings >
< netTcpBinding >
< binding name ="nonSessionBinding" >
<!-- 当前绑定的安全认证模式 -->
< security mode ="Message" >
<!-- 定义消息级安全性要求的类型,为证书 -->
< message clientCredentialType ="UserName" />
</ security >
</ binding >
</ netTcpBinding >
</ bindings >
4. 设置服务凭据值
<
behaviors
>
< serviceBehaviors >
< behavior name ="CalculatorServiceBehavior" >
< serviceCredentials >
<!-- 指定一个 X.509 证书,用户对认证中的用户名密码加密解密 -->
< serviceCertificate findValue ="CN=ejiyuan" x509FindType ="FindBySubjectDistinguishedName" storeLocation ="LocalMachine" storeName ="My" />
< clientCertificate >
<!-- 自定义对客户端进行证书认证方式 这里为 None -->
< authentication certificateValidationMode ="None" />
</ clientCertificate >
<!-- 自定义用户名和密码验证的设置 -->
< userNameAuthentication userNamePasswordValidationMode ="Custom" customUserNamePasswordValidatorType ="Wcf.Extensions.Security.UserNamePasswordValidator,Wcf.Extensions.Security" />
</ serviceCredentials >
</ behavior >
</ serviceBehaviors >
</ behaviors >
< serviceBehaviors >
< behavior name ="CalculatorServiceBehavior" >
< serviceCredentials >
<!-- 指定一个 X.509 证书,用户对认证中的用户名密码加密解密 -->
< serviceCertificate findValue ="CN=ejiyuan" x509FindType ="FindBySubjectDistinguishedName" storeLocation ="LocalMachine" storeName ="My" />
< clientCertificate >
<!-- 自定义对客户端进行证书认证方式 这里为 None -->
< authentication certificateValidationMode ="None" />
</ clientCertificate >
<!-- 自定义用户名和密码验证的设置 -->
< userNameAuthentication userNamePasswordValidationMode ="Custom" customUserNamePasswordValidatorType ="Wcf.Extensions.Security.UserNamePasswordValidator,Wcf.Extensions.Security" />
</ serviceCredentials >
</ behavior >
</ serviceBehaviors >
</ behaviors >
5. 自定义证书验证
通过继承自'System.IdentityModel.Selectors.UserNamePasswordValidator',然后我们重写里面的'Validate'方法来实现用户名密码认证逻辑
public
class
UserNamePasswordValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
{
public override void Validate( string userName, string password)
{
if (userName != " ejiyuan " || password != " 123456 " )
{
throw new System.IdentityModel.Tokens.SecurityTokenException( " Unknown Username or Password " );
}
}
}
{
public override void Validate( string userName, string password)
{
if (userName != " ejiyuan " || password != " 123456 " )
{
throw new System.IdentityModel.Tokens.SecurityTokenException( " Unknown Username or Password " );
}
}
}
6. 客户端代码
class
Program
{
static void Main( string [] args)
{
CalculatorClient client = new CalculatorClient();
// 指定认证的用户名和密码
client.ClientCredentials.UserName.UserName = " ejiyuan " ;
client.ClientCredentials.UserName.Password = " 123456 " ;
var q = client.add( 1 , 2 );
Console.WriteLine(client.add( 1 , 2 ));
Console.Read();
}
}
{
static void Main( string [] args)
{
CalculatorClient client = new CalculatorClient();
// 指定认证的用户名和密码
client.ClientCredentials.UserName.UserName = " ejiyuan " ;
client.ClientCredentials.UserName.Password = " 123456 " ;
var q = client.add( 1 , 2 );
Console.WriteLine(client.add( 1 , 2 ));
Console.Read();
}
}
7. 客户端配置信息(自动生成的)
<
system.serviceModel
>
< bindings >
< netTcpBinding >
< binding name ="NetTcpBinding_ICalculator" >
< security mode ="Message" >
< transport clientCredentialType ="Windows" protectionLevel ="EncryptAndSign" />
< message clientCredentialType ="UserName" />
</ security >
</ binding >
</ netTcpBinding >
</ bindings >
< client >
< endpoint address ="net.tcp://192.168.101.13:8000/calculatorservice"
binding ="netTcpBinding" bindingConfiguration ="NetTcpBinding_ICalculator"
contract ="ServiceReference1.ICalculator" name ="NetTcpBinding_ICalculator" >
< identity >
< certificate encodedValue ="AwAAAAEAAAAUAAAAgvtzbyRkxIGFn4UuyxD2+XJsJl8gAAAAAQAAAPQBAAAwggHwMIIBWaADAgECAhB/oj2gX287pUAmeLEVtWucMA0GCSqGSIb3DQEBBAUAMBIxEDAOBgNVBAMTB2VqaXl1YW4wHhcNMTAwNTI4MDkyNjQzWhcNMzkxMjMxMjM1OTU5WjASMRAwDgYDVQQDEwdlaml5dWFuMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfOgnw6Vs7gS52Gsud0WsuFOoDeF4+4DL1HFIpQupdExtIkWwY2v2/t/pWHRRvPE/aPf3M6axUYaT4pQqPXBHQR1lb0Hi6XLUGkzsEk7tjiEMEkpt+/8rQIdtXlmmry7yDixoX8PKEd5cGAISjEdbVKJqjQnC55rQXeDYlIXoqlwIDAQABo0cwRTBDBgNVHQEEPDA6gBCTu+dYQbdaauBGEk3SjJ5FoRQwEjEQMA4GA1UEAxMHZWppeXVhboIQf6I9oF9vO6VAJnixFbVrnDANBgkqhkiG9w0BAQQFAAOBgQA1jOywoJ5Xh6B6W3Vw7xPa9A6AH0WtedXPd4YbCU465UdKeP5G2HtKLpS20MnkU6lIh22lxMnb3WGZh70l5Sg1Hl0j/SklLKtOXzeQnVLaPundd9RS1TD/hHwVyu+89cr0866etfGwI9IDpwjhj5ixT3VUHI3eGrXRj+IGx8/W8Q==" />
</ identity >
</ endpoint >
</ client >
</ system.serviceModel >
< bindings >
< netTcpBinding >
< binding name ="NetTcpBinding_ICalculator" >
< security mode ="Message" >
< transport clientCredentialType ="Windows" protectionLevel ="EncryptAndSign" />
< message clientCredentialType ="UserName" />
</ security >
</ binding >
</ netTcpBinding >
</ bindings >
< client >
< endpoint address ="net.tcp://192.168.101.13:8000/calculatorservice"
binding ="netTcpBinding" bindingConfiguration ="NetTcpBinding_ICalculator"
contract ="ServiceReference1.ICalculator" name ="NetTcpBinding_ICalculator" >
< identity >
< certificate encodedValue ="AwAAAAEAAAAUAAAAgvtzbyRkxIGFn4UuyxD2+XJsJl8gAAAAAQAAAPQBAAAwggHwMIIBWaADAgECAhB/oj2gX287pUAmeLEVtWucMA0GCSqGSIb3DQEBBAUAMBIxEDAOBgNVBAMTB2VqaXl1YW4wHhcNMTAwNTI4MDkyNjQzWhcNMzkxMjMxMjM1OTU5WjASMRAwDgYDVQQDEwdlaml5dWFuMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfOgnw6Vs7gS52Gsud0WsuFOoDeF4+4DL1HFIpQupdExtIkWwY2v2/t/pWHRRvPE/aPf3M6axUYaT4pQqPXBHQR1lb0Hi6XLUGkzsEk7tjiEMEkpt+/8rQIdtXlmmry7yDixoX8PKEd5cGAISjEdbVKJqjQnC55rQXeDYlIXoqlwIDAQABo0cwRTBDBgNVHQEEPDA6gBCTu+dYQbdaauBGEk3SjJ5FoRQwEjEQMA4GA1UEAxMHZWppeXVhboIQf6I9oF9vO6VAJnixFbVrnDANBgkqhkiG9w0BAQQFAAOBgQA1jOywoJ5Xh6B6W3Vw7xPa9A6AH0WtedXPd4YbCU465UdKeP5G2HtKLpS20MnkU6lIh22lxMnb3WGZh70l5Sg1Hl0j/SklLKtOXzeQnVLaPundd9RS1TD/hHwVyu+89cr0866etfGwI9IDpwjhj5ixT3VUHI3eGrXRj+IGx8/W8Q==" />
</ identity >
</ endpoint >
</ client >
</ system.serviceModel >
备注:基于UserNamePasswordValidator的认证方式,Validator中可以知道相应的UserName和 Password,在Service中直接使用 OperationContext.Current.ServiceSecurityContext.PrimaryIdentity即可获取当前登录用 户信息。