当前位置:编程学习 > C#/ASP.NET >>

wcf 验证问题,求解

wcf有一个这样的情况
我有不同的后台系统,这些系统都需要调用wcf中的某些方法
1验证:需要不同的系统提交身份标识(通过session判断是否登陆)
2日志:需提供操作员的信息,方便记录日志

我现在的问题是:每一次系统调用方法前都需要调用依稀login(系统验证)
很麻烦,有没有更好的解决方法
能不能再服务端做些处理 wcf WCF  session 登录 --------------------编程问答-------------------- 可以用证书~(证书是服务端需要的,客户端只需要提供用户和密码)
 public static XXX.ContractsClient CreateService()
        {
            var client = new XXX.ContractsClient();

            client.ClientCredentials.UserName.UserName = UserName;
            client.ClientCredentials.UserName.Password = Password;
            client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
                    System.ServiceModel.Security.X509CertificateValidationMode.None;
//因为证书是我们用命令生成的,所以不强制认证证书的有效性(正式的证书是需要钱的)

            return client;

        }

var service=CreateService();
service.服务方法
service.close

http://www.cnblogs.com/chnking/archive/2008/10/07/1305811.html#_Toc212000476

http://www.54peixun.com/frankxulei/POST/0d815d22d6e64d56918419c9b6884e44
--------------------编程问答-------------------- 当第一次登录成功后,服务器端可以返回票证,客户端每次通信时都会传入票证来进行安全性验证。
当然你也可以每个通讯时都传入用户名密码。
客户可以通过行为扩展的方式自动传入票证(或者用户名密码)(SL不支持这种扩展,因为它只可以使用basicHttpBinding,而basicHttpBinding不支持扩展),服务器端也可以通过行为扩展的方式在调用WCF方法之前进行验证。



当然据蒋金楠大人所说,他不建议这么做,因为这样做违背了soa(不过我不太明白),如果需要权限控制建议使用Session。 --------------------编程问答-------------------- wcf支持单调,单例,会话。 会话就是我指的session。 --------------------编程问答-------------------- 我把我的代码发出来给你参考吧

服务器端代码:

注册行为:
/// <summary>
    /// 自定义行为
    /// </summary>
    public class CustomBehavior : BehaviorExtensionElement, IEndpointBehavior
    {
        public override Type BehaviorType
        {
            get { return typeof(CustomBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new CustomBehavior();
        }

        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {

        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
        {
            //错误事件的捕捉,我们可以在注册的事件中写错误日志
            endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ErrorHandler());

            //为WCF注册消息传入前、消息传出前的行为
            //通过这个行为中的“传入前”方法,我们进行用户密码的验证
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new IdentityHeaderInspector());
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }


注册的自定义行为:
 public class IdentityHeaderInspector : IDispatchMessageInspector  //服务器端观察者
    {
        /// <summary>
        /// 从客户端接收消息后
        /// 我们在这里进行用户权限的验证。
        /// 该方法在WCF方法前执行
        /// </summary>
        /// <param name="request"></param>
        /// <param name="channel"></param>
        /// <param name="instanceContext"></param>
        /// <returns></returns>
        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            //获取将要操作的webservice方法
            OperationContext ctx = OperationContext.Current;
            ServiceDescription hostDesc = ctx.Host.Description;
            ServiceEndpoint endpoint = hostDesc.Endpoints.Find(ctx.IncomingMessageHeaders.To);
            string operationName = ctx.IncomingMessageHeaders.Action.Replace(
              endpoint.Contract.Namespace + endpoint.Contract.Name + "/", "");


            //当访问Login方法时,是不需要在AfterReceiveRequest做权限验证的
            if (operationName == "Login")
            {
                //不做验证
            }
            else
            {
                int userIndex = OperationContext.Current.IncomingMessageHeaders.FindHeader("UserName", "xx");
                int pwdIndex = OperationContext.Current.IncomingMessageHeaders.FindHeader("Password", "xx");
                if (userIndex != -1 && pwdIndex != -1)
                {
                    string userName = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("UserName",
                        "http://richfit.com");
                    string password = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("Password",
                        "http://richfit.com");

                    UserInfoManager manager = new UserInfoManager();
                    int id = manager.Login(userName, password);

                    if (id > 0)
                    {
                        AppDomain.CurrentDomain.SetData("UserName", userName);  //保存用户编号用户名至上下文中,以供类库访问
                        AppDomain.CurrentDomain.SetData("UserId", id);
                    }
                    else
                    {
                        throw new FaultException<DataProviderError>(new DataProviderError(ErrorEnum.Verification, "Incorrect username or password!"));
                    }
                }
                else
                {
                    throw new FaultException<DataProviderError>(new DataProviderError(ErrorEnum.Verification, "Incorrect username or password!"));
                }
            }
            return null;
        }
    }


然后需要在配置文件中进行注册
<system.serviceModel>
    <!--增加扩展-->
    <extensions>
      <behaviorExtensions>
       
        <add name="CustomBehavior" type="Com.Richfit.A1.DMS.DataProvider.Services.Extensions.CustomBehavior, Com.Richfit.A1.DMS.DataProvider.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>
    <services>
      <service name="xx">
        <endpoint address="" binding="basicHttpBinding" contract="xx" behaviorConfiguration="CustomBehavior" ></endpoint>   <!--wsHttpBinding-->
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <!--2.增加行为 以供services使用-->
        <behavior name="CustomBehavior">
          <CustomBehavior />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior>
          <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
          <!--<serviceDebug includeExceptionDetailInFaults="true"/>-->
          
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>



然后是客户端:
public class AttachContextBehavior : BehaviorExtensionElement, IEndpointBehavior
    {
        public override Type BehaviorType
        {
            get { return typeof(AttachContextBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new AttachContextBehavior();
        }

        #region IEndpointBehavior
        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(new ClientIdentityHeaderInspector());
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new ServerIdentityHeaderInspector());
        }

        public void Validate(ServiceEndpoint endpoint)
        {
            return;
        }
        #endregion

        private class ClientIdentityHeaderInspector : 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)
            {
                string username = "lixiaoliang1";
                string userpwd = "123456";
                string module = "";

                MessageHeader<string> header = new MessageHeader<string>(username);
                request.Headers.Add(header.GetUntypedHeader("UserName", "xx"));

                header = new MessageHeader<string>(userpwd);
                request.Headers.Add(header.GetUntypedHeader("Password", "xx"));

                header = new MessageHeader<string>(module);
                request.Headers.Add(header.GetUntypedHeader("ModuleName", "http://richfit.com"));

                Console.WriteLine(string.Format("用户名:{0}。密码:{1}", username, userpwd));
                return null;
            }
        }

        private class ServerIdentityHeaderInspector : IDispatchMessageInspector  //服务器端观察者
        {
            public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
            {
                int index = OperationContext.Current.IncomingMessageHeaders.FindHeader("UserName",
          "");
                if (index != -1)
                {
                    string userName = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("UserName",
                        "");
                    string password = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("Password",
                        "");

                    Console.WriteLine("UserName:" + userName);
                    Console.WriteLine("Password:" + password);
                }
                return null;
            }

            public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
            {
            }
        }
    }


注:客户端和服务器端的扩展行为可以放一个cs文件中的。我是因为不是在一个项目所以才分开写的。 --------------------编程问答-------------------- wcf支持单调,单例,会话。 会话就是我指的session。

这个session和web的session不一样的远。 --------------------编程问答--------------------
引用 5 楼 nevermore_0923 的回复:
wcf支持单调,单例,会话。 会话就是我指的session。

这个session和web的session不一样的远。


嗯,启动会话模式时,服务器端会为每个客户端创建一个实例,并且该实例只有该客户端使用。当然如果开启aspNetCompatibilityEnabled后,客户端与服务器端就可以共享session了 --------------------编程问答--------------------
引用 4 楼 lixiaolian7 的回复:
我把我的代码发出来给你参考吧

服务器端代码:

注册行为:
C# code?1234567891011121314151617181920212223242526272829303132333435363738/// <summary>    /// 自定义行为    /// </summary>    public class CustomBehavior ……


这个可以试一下。
但是我目前的方法是
1每次实例化服务时,先调用login(userid,appkey)-两个参数,一个是操作员id,一个是系统key
2通过这两个参数获取两个对象 User,Module,然后用两个Session去保存这个两个对象。
3然后才是用户调用的方法。

现在又有个问题了,就是我采用的是AspNetCompatibilityRequirements的asp.net Session,我调试了下,这个session只能访问一次,第二次访问时就为null,如何解决? --------------------编程问答-------------------- 如果使用OperationContext.Current.SessionId,但是它应该只能保存一个对象数据,我希望的是可以保存多个数据,能普及下OperationContext.Current.SessionId一般可以做些什么? --------------------编程问答-------------------- OperationContext.Current.SessionId仅仅是一个会话标识而已,是会话自动生成的,不是用来保存数据的。 --------------------编程问答-------------------- 如果你使用了AspNetCompatibilityRequirements,那么就代表IIS会走ASP.NET管道,那么OperationContext就不会创建, 会为null了。 --------------------编程问答--------------------
引用 7 楼 yinxiongwu606 的回复:
引用 4 楼 lixiaolian7 的回复:我把我的代码发出来给你参考吧

服务器端代码:

注册行为:
C# code?1234567891011121314151617181920212223242526272829303132333435363738/// <summary>    /// 自定义行为    /// </summary>    publi……


我没有使用过asp.net兼容模式,请参考蒋金楠大人的demo 戳我 --------------------编程问答--------------------
引用 3 楼 lixiaolian7 的回复:
wcf支持单调,单例,会话。 会话就是我指的session。


这里的会话模式是指ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)
产生了一个OperationContext.Current.SessionId,那么可以根据这个session做些什么呢?举个例子哈,
只是拿一个session字符串可以做什么呢? --------------------编程问答--------------------
引用 12 楼 yinxiongwu606 的回复:
Quote: 引用 3 楼 lixiaolian7 的回复:

wcf支持单调,单例,会话。 会话就是我指的session。


这里的会话模式是指ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)
产生了一个OperationContext.Current.SessionId,那么可以根据这个session做些什么呢?举个例子哈,
只是拿一个session字符串可以做什么呢?

我理解,这个ID和asp.net中的SessionID是一回事,就是标识会话的唯一性。
更深入的解释,wcf的服务器端就是通过它来确定会话是属于哪个客户端的。
补充:.NET技术 ,  Web Services
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,