目标:
1.客户端与服务器端通信使用x509证书验证,但不用客户端安装证书。只需要服务器端配置好证书即可。
2.验证使用用户名密码形式。
操作:
(这里的测试使用wcf项目模板缺省的服务,即只要新建一个使用vs2008自动生成的wcf项目就行了,
它会自动生成有一个GetData方法,我就用这个方法进行测试)
1.新建WCF服务应用程序.
1.1生成一个服务器证书:运行Visual Studio 2008 命令提示工具:
输入:makecert -r -pe -n "CN=MyServer" -sr LocalMachine -ss My -sky exchange执行。
-sr LocalMachine 请一定保存到LodcalMachine中.目的就是到时如果你部署这个wcf服务的时候可以让IIS找到证书,
反之,IIS会报找不到x509证书.
2.配置web.config文件:
这里要注意的是把storeLocation设为LocalMachine,原因也是到时需要部署的时候可以免掉很多麻烦,因为以后发布到iis时很可以不能正常验证到证书的私钥.
1: <system.serviceModel>
2: <bindings>
3: <wsHttpBinding>
4: <binding name="NewBinding0">
5: <security>
6: <message clientCredentialType="UserName" />
7: </security>
8: </binding>
9: </wsHttpBinding>
10: </bindings>
11: <services>
12: <service behaviorConfiguration="WcfService2.Service1Behavior"
13: name="WcfService2.Service1">
14: <endpoint address="" binding="wsHttpBinding" bindingConfiguration="NewBinding0"
15: contract="WcfService2.IService1">
16: </endpoint>
17: <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
18: </service>
19: </services>
20: <behaviors>
21: <serviceBehaviors>
22: <behavior name="WcfService2.Service1Behavior">
23: <serviceMetadata httpGetEnabled="true" />
24: <serviceDebug includeExceptionDetailInFaults="false" />
25: <serviceCredentials>
26: <clientCertificate>
27: <authentication certificateValidationMode="None" />
28: </clientCertificate>
29: <serviceCertificate findValue="MyServer" storeLocation="LocalMachine" x509FindType="FindBySubjectName" />
30: <userNameAuthentication userNamePasswordValidationMode="Custom"
31: customUserNamePasswordValidatorType="WcfService2.MyUserNamePasswordValidator,WcfService2" />
32: </serviceCredentials>
33: </behavior>
34: </serviceBehaviors>
35: </behaviors>
36: </system.serviceModel>
3.建造验证客户端用户名和密码的方法.
这里注意的是必须与web.config文件中的customUserNamePasswordValidatorType=中的内容一致,
格式是:"命名空间.方法名,命名空间"
实际项目应用中这里应该是从数据库里确认用客是否合法。
1: namespace WcfService2
2: {
3: public class MyUserNamePasswordValidator : UserNamePasswordValidator
4: {
5: public override void Validate(string userName, string password)
6: {
7: if (userName != "jac" || password != "jac")
8: {
9: throw new SecurityTokenException("Unknown Username or Password");
10: }
11: }
12: }
13: }
至此,wcf服务配置完成。
4.新建一个asp.net项目,并添加服务引用这个wcf服务.
5.修改asp.net项目的web.config文件(一定要在引用wcf服务后).
添加一个endpointBehaviors:
1: <behaviors>
2: <endpointBehaviors>
3: <behavior name="jacBehavior">
4: <clientCredentials>
5: <serviceCertificate>
6: <authentication certificateValidationMode="None" />
7: </serviceCertificate>
8: </clientCredentials>
9: </behavior>
10: </endpointBehaviors>
11: </behaviors>
然后让它生效,
1: <endpoint address="http://localhost/Service1.svc" behaviorConfiguration="jacBehavior"
2: binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
3: contract="ServiceReference1.IService1" name="WSHttpBinding_IService1">
以下是完整的asp.net客户端的web.config文件的system.serviceModel部份
1: <system.serviceModel>
2: <behaviors>
3: <endpointBehaviors>
4: <behavior name="jacBehavior">
5: <clientCredentials>
6: <serviceCertificate>
7: <authentication certificateValidationMode="None" />
8: </serviceCertificate>
9: </clientCredentials>
10: </behavior>
11: </endpointBehaviors>
12: </behaviors>
13: <bindings>
14: <wsHttpBinding>
15: <binding name="WSHttpBinding_IService1" closeTimeout="00:01:00"
16: openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
17: bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
18: maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
19: textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
20: <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
21: maxBytesPerRead="4096" maxNameTableCharCount="16384" />
22: <reliableSession ordered="true" inactivityTimeout="00:10:00"
23: enabled="false" />
24: <security mode="Message">
25: <transport clientCredentialType="Windows" proxyCredentialType="None"
26: realm="" />
27: <message clientCredentialType="UserName" negotiateServiceCredential="true"
28: algorithmSuite="Default" establishSecurityContext="true" />
29: </security>
30: </binding>
31: </wsHttpBinding>
32: </bindings>
33: <client>
34: <endpoint address="http://j-8de9be98d1184/Service1.svc" behaviorConfiguration="jacBehavior"
35: binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
36: contract="ServiceReference1.IService1" name="WSHttpBinding_IService1">
37: <identity>
38: <certificate encodedValue="AwAAAAEA.....................eGtnWJsvtFQsEuzDYw==" />
39: </identity>
40: </endpoint>
41: </client>
42: </system.serviceModel>
6.调用.
1: ServiceReference1.Service1Client sc = new WebApplication1.ServiceReference1.Service1Client();
2: sc.ClientCredentials.UserName.UserName = "jac";
3: sc.ClientCredentials.UserName.Password = "jac";
4: Label1.Text = sc.GetData(22);
完成.
工程文件下载:http://FunSL.com
为了便使这种方式自由度更高,以下是服务器端的纯代码版本:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.ServiceModel;
6: using System.IdentityModel.Selectors;
7: using System.ServiceModel.Description;
8:
9: namespace wcf.username
10: {
11: class Program
12: {
13: static void Main(string[] args)
14: {
15: EndpointAddress endp = new EndpointAddress("http://localhost/myservice");
16:
17: WSHttpBinding ws = new WSHttpBinding();
18: ws.Security.Mode = SecurityMode.Message;
19: ws.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
20:
21: ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
22: behavior.HttpGetEnabled = true;
23: behavior.HttpGetUrl = new Uri("http://localhost/myservice/mex");
24:
25: ServiceHost sh = new ServiceHost(typeof(serverdo), new Uri(endp.ToString()));
26:
27: sh.Description.Behaviors.Add(behavior);
28:
29: sh.Credentials.ClientCertificate.Authentication.CertificateValidationMode =
30: System.ServiceModel.Security.X509CertificateValidationMode.None;
31:
32:
33: sh.Credentials.UserNameAuthentication.UserNamePasswordValidationMode =
34: System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
35:
36: sh.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new checkUserName();
37:
38: sh.Credentials.ServiceCertificate.SetCertificate(
39: System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
40: System.Security.Cryptography.X509Certificates.StoreName.My,
41: System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName, "MyServer");
42:
43: sh.AddServiceEndpoint(typeof(Iservice), ws, endp.ToString());
44: sh.Open();
45:
46: Console.WriteLine("ok");
47: Console.Read();
48: }
49: }
50:
51: class checkUserName:UserNamePasswordValidator
52: {
53: public override void Validate(string userName, string password)
54: {
55: if (userName != "jac" && password != "jac")
56: {
57: throw new FaultException("userName and passWord be error");
58: }
59: }
60: }
61:
62: [ServiceContract]
63: interface Iservice
64: {
65: [OperationContract]
66: string test(string msg);
67: }
68:
69: class serverdo:Iservice
70: {
71: #region Iservice 成员
72:
73: public string test(string msg)
74: {
75: Console.WriteLine("user inter:" + msg);
76:
77: return "user inter:" + msg;
78: }
79:
80: #endregion
81: }
82:
83: }