[WCF安全2]使用wsHttpBinding构建UserName授权的WCF应用程序,非SSL
上一篇文章中介绍了如何使用basicHttpBinding构建UserName授权的WCF应用程序,本文将为您介绍如何使用wsHttpBinding构建非SSL的UserName安全授权的WCF应用程序。
与上篇文章一样,同样将该示例分为服务端与客户端介绍。
1. 服务端
(1) 实现CustomUserNameValidator
与上篇文章一样,需要首先实现CustomUserNameValidator,如果还不知道如何生成CustomUserNameValidator,请参考上一篇文章。
1 public class CustomUserNameValidator : UserNamePasswordValidator 2 { 3 private const string USERNAME_ELEMENT_NAME = "userName"; 4 5 private const string PASSWORD_ELEMENT_NAME = "password"; 6 7 private const string FAULT_EXCEPTION_MESSAGE = "UserName or Password is incorrect!"; 8 9 public override void Validate(string userName, string password) 10 { 11 Guarder.Guard.ArgumentNotNull(userName) 12 .ArgumentNotNull(password); 13 var validateUserName = ConfigurationManager.AppSettings[USERNAME_ELEMENT_NAME]; 14 var validatePassword = ConfigurationManager.AppSettings[PASSWORD_ELEMENT_NAME]; 15 var validateCondition = userName.Equals(validateUserName) && password.Equals(validatePassword); 16 if (!validateCondition) 17 { 18 throw new FaultException(FAULT_EXCEPTION_MESSAGE); 19 } 20 } 21 }
(2) 注册服务端证书
这个环节是在上一篇文章中没有的,后文中要介绍到,我们使用了Message的方式加密消息内容,因此,我们需要将传输内容加密,因此需要使用证书进行加密。我们使用微软自带的"makecert.exe"命令行工具向currentUser注册一个证书。使用Visual Studio自带的命令行工具执行下面命令行。
1 makecert.exe -sr CurrentUser -ss My -a sha1 -n CN=ServerCert -sky exchange –pe
(3) 完成服务端配置文件
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <appSettings> 4 <add key="username" value="username"/> 5 <add key="password" value="password"/> 6 </appSettings> 7 <startup> 8 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> 9 </startup> 10 <system.serviceModel> 11 <behaviors> 12 <serviceBehaviors> 13 <behavior name="securityBehavior"> 14 <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> 15 <serviceDebug includeExceptionDetailInFaults="true" /> 16 <serviceCredentials> 17 <serviceCertificate 18 findValue="ServerCert" 19 x509FindType="FindBySubjectName" 20 storeLocation="CurrentUser" 21 storeName="My"/> 22 <userNameAuthentication 23 userNamePasswordValidationMode="Custom" 24 customUserNamePasswordValidatorType="TimeSynchronizeServer.CustomUserNameValidator,TimeSynchronizeServer"/> 25 </serviceCredentials> 26 </behavior> 27 </serviceBehaviors> 28 </behaviors> 29 <bindings> 30 <wsHttpBinding> 31 <binding name="securityMessageBinding"> 32 <security mode="Message"> 33 <message clientCredentialType="UserName"/> 34 </security> 35 </binding> 36 </wsHttpBinding> 37 </bindings> 38 <services> 39 <service name="TimeSynchronizeServer.TimeSynchronizeService" 40 behaviorConfiguration="securityBehavior"> 41 <endpoint address="http://127.0.0.1/12216/TimeSynchronize" 42 binding="wsHttpBinding" bindingConfiguration="securityMessageBinding" 43 contract="TimeSynchronizeServer.ITimeSynchronizeService"> 44 <identity> 45 <dns value="ServerCert" /> 46 </identity> 47 </endpoint> 48 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> 49 <host> 50 <baseAddresses> 51 <add baseAddress="http://localhost:8733/Design_Time_Addresses/TimeSynchronizeServer/TimeSynchronizeService/" /> 52 </baseAddresses> 53 </host> 54 </service> 55 </services> 56 </system.serviceModel> 57 </configuration>
其中serviceCredentials节点中添加了serviceCertificate节点,该节点用来指定使用的证书。findValue,此attribute指定的是(2)中命令行中CN=右侧的字符。userNameAuthentication制定了自定义的Validator。binding节点中使用了wsHttpBinding,并将传输的加密方式指定为Message,并制定UserName为授权方式。
2. 客户端
(1) 完成客户端配置文件
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <startup> 4 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> 5 </startup> 6 <appSettings> 7 <add key="userName" value="username"/> 8 <add key="password" value="password"/> 9 </appSettings> 10 <system.serviceModel> 11 <bindings> 12 <wsHttpBinding> 13 <binding name="securityMessageBidning"> 14 <security mode="Message"> 15 <message clientCredentialType="UserName"/> 16 </security> 17 </binding> 18 </wsHttpBinding> 19 </bindings> 20 <client> 21 <endpoint address="http://127.0.0.1/12216/TimeSynchronize/" 22 binding="wsHttpBinding" bindingConfiguration="securityMessageBidning" 23 contract="ITimeSynchronizeService" name="DefaultBinding_ITimeSynchronizeService_ITimeSynchronizeService"> 24 <identity> 25 <dns value="ServerCert"/> 26 </identity> 27 </endpoint> 28 </client> 29 </system.serviceModel> 30 </configuration>
需要注意的是,binding节点需要与服务端配置文件中的binding配置相同。特别要注意的是,endpoint节点中的identity节点中的dns的value需要与证书名称相同,否则会抛出一个异常,具体问题我还没搞明白,不过搜索了一下,发现这样能够解决问题。
(2) 完成客户端调用代码
1 private const string USERNAME = "userName"; 2 private const string PASSWORD = "password"; 3 4 static void Main ( string[] args ) 5 { 6 var proxy = new TimeSynchronizeServiceClient ( ); 7 var userName = ConfigurationManager.AppSettings[USERNAME]; 8 var password = ConfigurationManager.AppSettings[PASSWORD]; 9 proxy.ClientCredentials.UserName.UserName = userName; 10 proxy.ClientCredentials.UserName.Password = password; 11 proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = 12 X509CertificateValidationMode.None; 13 var time = proxy.GetTime ( ); 14 var builder = new StringBuilder ( ); 15 builder.Append ( "Server time is:" ).Append ( " " ).Append ( time ); 16 var message = builder.ToString ( ); 17 Console.WriteLine ( message ); 18 Console.ReadKey ( ); 19 }
OK,大功告成!