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不一样的远。 --------------------编程问答--------------------
嗯,启动会话模式时,服务器端会为每个客户端创建一个实例,并且该实例只有该客户端使用。当然如果开启aspNetCompatibilityEnabled后,客户端与服务器端就可以共享session了 --------------------编程问答--------------------
这个可以试一下。
但是我目前的方法是
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了。 --------------------编程问答--------------------
我没有使用过asp.net兼容模式,请参考蒋金楠大人的demo 戳我 --------------------编程问答--------------------
这里的会话模式是指ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)
产生了一个OperationContext.Current.SessionId,那么可以根据这个session做些什么呢?举个例子哈,
只是拿一个session字符串可以做什么呢? --------------------编程问答--------------------
我理解,这个ID和asp.net中的SessionID是一回事,就是标识会话的唯一性。
更深入的解释,wcf的服务器端就是通过它来确定会话是属于哪个客户端的。
补充:.NET技术 , Web Services