当系统越做到后面 要求也就越高,简单的wcf的调用的功能似乎不能满足一些业务需求,例如:传递的参数数据比较大,跨平台的调用,客户端调用的限制,也就是说安全性的考虑,在安全性测试的过程中 甲方说 你们这个服务安全性太低,谁都能调用。、。。所以确实要考虑了安全性的因素,所以索性的花了几天时间来调研了一下wcf的安全性的相关知识。
在百度资料的过程中发现最多的莫过于 wcf 的message 和transport ,其实大部分都和网上差不多,但是极为重要的一点是不一样的的我觉得这个是极容易被大家忽略的 ,我也是实验了好多次 才发现的。
message方式 是信息层方式很显然 重点是 信息这种方式就没用这种传用户名+密码的方式,必须要用证书验证,证书虽说安全一些,但是安装一个客户端 还要对应的安装一个证书,不免有一些麻烦,所以我是没有使用这种方式的,网上所谓message也可以用用户名和密码 那绝对不可能的,触发你装了证书。
transport方式,这个是传输层的安全,这个就是传统的在head中验证参数,但是 如果你的wcf是部署在iis ,那就恭喜你,如果用了这种安全性,在iis上必须要加身份验证,那我就搞不懂了,我既然都要加自己的验证了 你还必须让我把 wcf 网站设置身份,那不是脱了裤子放屁吗,我客户端 是java程序, 那我还得先用java 实现身份验证,然后我还要在实现transport,所以我直接也pass掉了。但是transport 还是要比message 要更满足我现在的需求 我先把transport的实现贴出来了吧,还有就是basicHttpBinding就可以满足 安全性的配置。下面是我从微软官网考的例子作为参考,如果你用 transport 就用<transport > 如果你用message 就用<message >标签。
security 包含:None/Transport/Message/TransportWithMessageCredential/TransportCredentialOnly 网上又他们的区别
包括每个选项的区别。自己可以查到很多 例如https://www.aliyun.com/zixun/content/2_6_525059.html 这个就介绍的很详细
<basicHttpBinding>
<binding name="basicHttpBindingConfig" maxReceivedMessageSize="2147483647" transferMode="Buffered" sendTimeout="00:10:00">
<!--<security mode="None/Transport/Message/TransportWithMessageCredential/TransportCredentialOnly">
<transport clientCredentialType="None/Basic/Digest/Ntlm/Windows/Certificate"
proxyCredentialType="None/Basic/Digest/Ntlm/Windows"
realm="string" />
<message
algorithmSuite="Basic128/Basic192/Basic256/Basic128Rsa15/Basic256Rsa15/TripleDes/TripleDesRsa15/Basic128Sha256/Basic192Sha256/TripleDesSha256/Basic128Sha256Rsa15/Basic192Sha256Rsa15/Basic256Sha256Rsa15/TripleDesSha256Rsa15"
clientCredentialType="UserName/Certificate"/>
</security>-->
</binding>
</basicHttpBinding>
这个是用custom 和证书的方式 的配置,请参考 ,其实我已经实现了,和我上面说的一样 就是因为要加身份验证 不然会报什么basicHttpBinding 验证不能是 anymous 这个错, 那就是验证的问题。 我用 transport的Basic 那就用 下面的userNameAuthentication标签 在后台 自定义customUserNamePasswordValidatorType 类并且赋值好命名空间。 如果要用证书 方式 那就用serviceCertificate 这个标签内容 在
<!--<serviceCredentials>
-->
<!--指定验证方式为Custom,表示自定义,既然是自定义的,就要指出用哪个类进行用户名密码验证,这里指定了Server程序集中的Server.Validator类,注意这里类完整名称的写法-->
<!--
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Web.Server.CustUsernamepwdValidator, Web.Server" />
-->
<!--指定用于数据加密的证书,LocalMachine表示本地计算机,Root表示受信任根证书颁发机构,192.168.90.81是证书标题(因为做证书时没指定标题,所以使用者默认就是标题),FindBySubjectName表示按标题查找-->
<!--
<serviceCertificate storeLocation="LocalMachine" storeName="Root" findValue="WCFTest" x509FindType="FindBySubjectName" />
</serviceCredentials>-->
而最后 我没有用上述的方法 而是使用的 wcf 的AOP 方式,也就是请求拦截, 核心是和java web中的filter 类似 也就是说,每个请求都会进入一个方法进行拦截, 做一些安全的验证 如果验证通过才继续总服务的方法 ,下面先贴出wcf 的配置文件。
在servicemode 下面加一下的内容, 可以理解为给wcf 服务加扩展,注册一个扩展, name 自定义 ,type 为你的类型,和命名空间,后面的照写。
<extensions>
<behaviorExtensions><add name="messageInspector" type="Web.Server.ServiceBehavior, Web.Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
下面是在 behavior 加入刚刚自己自定义的标签;
<behavior name="metadataBehavior">
<messageInspector />
</behavior >
最后然后 在service 绑定上 behaviorConfiguration
<services>
<service name="Web.Server.WeatherService" behaviorConfiguration="metadataBehavior">
接下来就要写后台代码了 这个代码原封不动靠过来了 用就好了 注意 自己要 定义一个 MessageInspector 这个拦截器的类
public class ServiceBehavior : BehaviorExtensionElement,IServiceBehavior
{
#region BehaviorExtensionElement
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageInspector());
}
}
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
#endregion
#region IServiceBehavior Members
public override Type BehaviorType
{
get { return typeof(ServiceBehavior); }
}
protected override object CreateBehavior()
{
return new ServiceBehavior();
}
#endregion
}
//拦截器 注意进行实际操作的类, 继承IDispatchMessageInspector接口,AfterReceiveRequest这个是在请求之后会进行这个方法执行,return null 代码成功, thorow异常 代表禁止调用
public class MessageInspector : IDispatchMessageInspector
{
object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
string un = "";
string ps = "";
// 栓查验证信息
try
{
un = request.Headers.GetHeader<string>("username", "随便写和客户端一直就行");
ps = request.Headers.GetHeader<string>("password", "随便写和客户端一直就行");
}
catch (Exception ex)
{
throw new Exception("非法请求,不予提供服务");
}
if (CheckAuth(un, ps))
{
return null;
}
else
{
throw new Exception("请求失败,请通知管理员检查验证");
}
}
由于很赶时间,所以就没用写的太详细,实现起来很曲折, 但是我觉得AOP 这种面向切面的方式还是值得 使用的,安全性足够了,比较wcf 是soap 方式的请求,封装成的xml 普通的模拟拦截 还是有点苦难的。 java客户端调用 就用axis 这个就能调用,记得要把验证的 用户名 和密码 放在头部节点里, 要保证 字段以及命名空间与服务器的一直,如果后面有时间 会继续完善这个地方