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();
}
}
3. 设置安全验证模式
<
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
>
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
"
);
}
}
}
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();
}
}
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
>
备注:基于UserNamePasswordValidator的认证方式,Validator中可以知道相应的UserName和Password,在Service中直接使用OperationContext.Current.ServiceSecurityContext.PrimaryIdentity即可获取当前登录用户信息。