WCF 的安全性以及很健壮了,但是因为我的是虚拟空间,只能挂在iis上,也不能https,所以什么证书啥的都用不上,好像有个用户名密码认证也需要证书的情况下才能.
传统的办法是通过在服务的操作中从OperationContext.Current.IncomingMessageHeaders来获取Header中的内容,而在客户端在OperationContext.Current.OutgoingMessageHeaders中添加MessageHeader,但这种方式需要在每次客户端调用和每个服务操作中都增加类似代码片断,比较麻烦。
后来了解到可以通过自定义Behavior来实现,服务端和客户端都要实现一个IEndpointBehavior, 验证需要放在服务端,不然就没意义了
下面是服务端的代码
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Configuration; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.Text; using System.Threading.Tasks; namespace Blogs.Service { public class AttachAuthenticationBehavior : IDispatchMessageInspector, IEndpointBehavior { #region IDispatchMessageInspector成员 public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext) { string username = GetHeaderValue("OperationUserName"); string pwd = GetHeaderValue("OperationPwd"); if (username == "admin" && pwd == "admin") { } else { throw new Exception("操作中的用户名,密码不正确!"); } return null; } public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { } #endregion public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new AttachAuthenticationBehavior()); } public void Validate(ServiceEndpoint endpoint) { } private string GetHeaderValue(string key) { int index = OperationContext.Current.IncomingMessageHeaders.FindHeader(key, "http://tempuri.org"); if (index >= 0) { return OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index).ToString(); } return null; } } }
下面是服务端的web.config 部分配置,这个服务可能没有配置完整
<system.serviceModel> <services> <service name="Blogs.Service.DataService" behaviorConfiguration="MyServiceTypeBehaviors"> <endpoint behaviorConfiguration="withMessageInspector" address="" binding="basicHttpBinding" bindingConfiguration="LargeDataTransferServicesBinding" contract="Blogs.IService.IDataService" /> </service> </services> <behaviors> <endpointBehaviors> <behavior name="withMessageInspector"> <AuthenticationBehavior /> </behavior> </endpointBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> <client /> <extensions> <behaviorExtensions> <add name="AuthenticationBehavior" type="Blogs.Service.AuthenticationBehaviorExtensionElement,Blogs.Service"/> </behaviorExtensions> </extensions> </system.serviceModel>
下面来看看客户端
public class AuthenticationEndpointBehavior : IClientMessageInspector, IEndpointBehavior { #region IClientMessageInspector成员 public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { } public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) { MessageHeader userNameHeader = MessageHeader.CreateHeader("OperationUserName", "http://tempuri.org", "admin", false, ""); MessageHeader pwdNameHeader = MessageHeader.CreateHeader("OperationPwd", "http://tempuri.org", "admin", false, ""); request.Headers.Add(userNameHeader); request.Headers.Add(pwdNameHeader); Console.WriteLine(request); return null; } #endregion #region IDispatchMessageInspector成员 public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext) { throw new NotImplementedException(); } public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { } #endregion public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(this); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } }
在写一个服务帮助类方法
#region 获取服务接口对象 private IService _service; private IService _dal; private ManualResetEvent signal = new ManualResetEvent(false); private ChannelFactory<IService> _channelFactory; /// <summary> /// 获取用于对服务进行透明的远程访问的服务协定。 /// </summary> public virtual IService Service { get { if (_dal != null) { return _dal; } if (_channelFactory == null) { _channelFactory = new ChannelFactory<IService>("*"); _channelFactory.Endpoint.EndpointBehaviors.Add(new AuthenticationEndpointBehavior()); } if (_service != null) { var ch = (IChannel)_service; if (ch.State == CommunicationState.Closing || ch.State == CommunicationState.Closed || ch.State == CommunicationState.Faulted) { //通道关闭或失败后创建新的通道。 signal.Reset(); _service = _channelFactory.CreateChannel(); var svc = ((IChannel)_service); svc.Opened += new EventHandler((_, __) => signal.Set()); svc.Open(TimeSpan.FromSeconds(30)); if (_service == null) { throw new CommunicationException(String.Format("打开服务{0}超时,当前的超时时间为30秒。", _service.GetType().Name)); } return _service; } else { //等待通道创建完成 if (signal.WaitOne(30000)) return _service; throw new CommunicationException(String.Format("打开服务{0}超时,当前的超时时间为30秒。", _service.GetType().Name)); } } signal.Reset(); _service = _channelFactory.CreateChannel(); var channel = ((IChannel)_service); channel.Opened += new EventHandler((_, __) => signal.Set());//打开通道后通知正在等待打开的线程。 //RegisterEvents(channel); channel.Open(TimeSpan.FromSeconds(30)); if (_service == null) { throw new CommunicationException(String.Format("打开服务{0}超时,当前的超时时间为30秒。", _service.GetType().Name)); } return _service; } set { _dal = value; } } #endregion