以前项目解决方案中,用http协议的asmx Web service作服务器数据访问入口,在SoapHeader中写入用户名和加盐密码进行身份认证。

http asmx服务是明文传输,传输过程中数据很容易被截取、篡改。在内网使用、用户量小、安全问题不严重的情况下运行几年,没有出过大的问题。

但随着项目的发展,需要对服务器进行改造,升级成更高级的安全方式。

最先想到的是将http协议改用https,解决数据明文传输泄密以及有可能被篡改,造成服务器数据严重混乱问题。

但是,https传输存在两个问题:1是客户机要安装证书,增加用户操作复杂度;2是运行环境中客户机与服务器之间可能存在安全接入边界系统,传输层加密存在诸多问题和不确定性。

因此,决定采用WCF Message加密方案,不使用Transport安全模式,保持http传输协议。这样客户机与服务器通信数据可以顺利通过安全接入边界系统,一套方案可以适应各种网络结构、适应不同的网络环境,安装、布署、使用简单,对技术支持、维护人员培训成本低。虽然可能Message安全方案效率低些,但在网络速度飞速提升的背景下,对用户体验不会造成大的影响。

另外,采用Windows Service寄宿方案,不涉及IIS的安装、配置,降低技术维护复杂性,对安装、维护人员要求也低些,相对来讲,可控度更高,服务器安全性、稳定性也有提升。

实现WCF Message安全方案:

一,服务证书准备

1,创建证书:Visual Studio Tools命令提示符下执行(假设证书名为WCFServerCA,实际可以替换为自己想要名)

makecert -r -pe -n "CN=WCFServerCA" -sr LocalMachine -ss My -sky exchange

2,导出证书:运行MMC控制台,从本地计算机节点证书内导出WCFServerCA证书,保存为带私钥的证书文件,扩展名为.pfx。导出时要输入密码,该密码要在导入时使用。
3,导入证书:在运行服务的计算机上导入该.pfx证书,导入到本地计算机节点受信任的根证书颁发机构和受信任人子节点下。此证书可以保存起来,供其他服务器安装时使用。

二,建契约项目 Contract。服务契约单独出来,由服务、宿主、客户端引用,在编程上自由度更高。

namespace Contract
{
[ServiceContract]
public interface IService
{
[OperationContract]
string Hello(string name);
}
}

三,建WCF服务库项目 WcfLib。包含服务类和UserName验证类。

OperationContext.Current是WCF中的重要对象,代表服务操作上下文。通过此对象,可以获取与服务操作有关的各种数据,如用OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name获取到客户端验证用户名。

服务实现:

using System.ServiceModel;
using System.ServiceModel.Channels;
namespace WcfLib
{
public class Service : IService
{
public string Hello(string name)
{
//提供方法执行的上下文环境
OperationContext context = OperationContext.Current;
//UserName验证的用户名
string username = context.ServiceSecurityContext.PrimaryIdentity.Name;
//获取传进的消息属性
MessageProperties properties = context.IncomingMessageProperties;
//获取消息发送的远程终结点IP和端口
RemoteEndpointMessageProperty endpoint = properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
Console.WriteLine(string.Format("Hello {0}, You are from {1}:{2}", name, endpoint.Address, endpoint.Port));
return string.Format("Hello {0},You are from {1}:{2}", name, endpoint.Address, endpoint.Port);
}
}
}

UserName验证实现:

namespace WcfLib
{
public class CustomUserNamePasswordValidator : UserNamePasswordValidator
{
private const string FAULT_EXCEPTION_MESSAGE = "用户登录失败!";
public override void Validate(string userName, string password)
{
bool validateCondition = false;
validateCondition = userName == "z" && password == "";
if (!validateCondition)
{
throw new FaultException(FAULT_EXCEPTION_MESSAGE);
}
}
}
}

服务配置文件:

 <system.serviceModel>
<services>
<!--name需为服务类的完全限定名-->
<service name="WcfLib.Service" behaviorConfiguration="MessageAndUserNameBehavior">
<host>
<baseAddresses>
<!--服务基地址,应以/结尾,以便与endpoint的address组合-->
<add baseAddress = "http://localhost:8733/" />
</baseAddresses>
</host>
<!-- Service Endpoints -->
<!-- 除非完全限定,否则地址相对于上面提供的基址-->
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="MessageAndUserName" contract="Contract.IService">
<!--服务器和客户端dns标识value值应一致,如删除,客户端dns值应与证书名称相符(更新服务引用时可自动获取) -->
<identity>
<dns value="WCFServerCA"/>
</identity>
</endpoint>
<!-- Metadata Endpoints -->
<!-- 元数据交换终结点供相应的服务用于向客户端做自我介绍。 -->
<!-- 此终结点不使用安全绑定,应在部署前确保其安全或将其删除-->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="MessageAndUserName">
<!--使用消息安全自定义用户名和密码验证-->
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="MessageAndUserNameBehavior">
<!-- 为避免泄漏元数据信息,
请在部署前将以下值设置为 false -->
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
<!-- 要接收故障异常详细信息以进行调试,
请将以下值设置为 true。在部署前设置为 false
以避免泄漏异常信息 -->
<serviceDebug includeExceptionDetailInFaults="False" />
<!--指定服务证书-->
<serviceCredentials>
<!--根据证书名称查找服务证书 到TrustedPeople受信任人里面去查找-->
<serviceCertificate storeName="TrustedPeople" x509FindType="FindBySubjectName" findValue="WCFServerCA" storeLocation="LocalMachine" />
<!--对服务证书的认证模式 本配置为服务端,当windows service寄宿时,到可信任人区去查找认证-->
<clientCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</clientCertificate>
<!--自定义用户密码身份验证程序集-->
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfLib.CustomUserNamePasswordValidator,WcfLib"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

五,实现Windows Service寄宿。

1,新建控制台项目WinServiceHosting,添加 System.ServiceModel.dll 的引用,添加 WCF 服务类库(WcfLib)的项目引用(此例服务类库名WcfServiceLib),添加服务契约项目Contract引用。

2,项目中添加Window服务类,起名WCFServiceMgr.cs,服务寄宿代码:

using System;
using System.ServiceModel;
using System.ServiceProcess;
namespace WinServiceHosting
{
partial class WCFServiceMgr : ServiceBase
{
public static string Name = "WCF服务Windows Service寄宿";
static object syncRoot = new object();//同步锁
ServiceHost srvHost = null; //寄宿服务对象
public WCFServiceMgr()
{
InitializeComponent();
this.ServiceName = Name;
}
protected override void OnStart(string[] args)
{
// 启动服务。
try
{
srvHost = new ServiceHost(typeof(WcfSumServiceLib.Sum));
if (srvHost.State != CommunicationState.Opened)
{
srvHost.Open();
//此处写启动日志
}
}
catch (Exception)
{
//此处写错误日志
}
}
protected override void OnStop()
{
// 停止服务
if (srvHost!=null)
{
srvHost.Close();
srvHost = null;
//写服务停止日志
}
}
}
}

3,在WCFServiceMgr设计界面上右键,添加安装程序,项目里生成ProjectInstaller.cs文件。

4,选中serviceInstaller1,设定服务标识名称DisplayName:WCF服务Windows Service寄宿(此名称在控制面板服务中显示);再设定系统服务标识名称ServiceName:WCFServiceMgr(此名称用于系统标识);再设定服务启动方式StartType为Automatic,即自动启动。选中serviceProcessInstaller1,指定用来运行此服务的帐户类型Account为LocalSystem。

5,通过控制台程序实现参数化安装和卸载服务 , -i表示安装,-u表示卸载。服务配置与服务类库配置相同

using System;
using System.Configuration.Install;
using System.ServiceProcess;
namespace WinServiceHosting
{
class Program
{
static void Main(string[] args)
{
ServiceController service = new ServiceController(WCFServiceMgr.Name);
if (args.Length==)
{
//运行服务
ServiceBase[] serviceBasesToRun = new ServiceBase[] { new WCFServiceMgr() };
ServiceBase.Run(serviceBasesToRun);
}
else if(args[].ToLower()=="/i" || args[].ToLower()=="-i")
{
//安装服务
if (!IsServiceExisted("WCFServiceMgr"))
{
try
{
string[] cmdline = { };
string serviceFileName = System.Reflection.Assembly.GetExecutingAssembly().Location;
TransactedInstaller transactedInstaller = new TransactedInstaller();
AssemblyInstaller assemblyInstaller = new AssemblyInstaller(serviceFileName, cmdline);
transactedInstaller.Installers.Add(assemblyInstaller);
transactedInstaller.Install(new System.Collections.Hashtable());
TimeSpan timeout = TimeSpan.FromMilliseconds( * );
service.Start();
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
}
catch (Exception)
{
}
}
}
else if (args[].ToLower() == "/u" || args[].ToLower() == "-u")
{
//删除服务
try
{
if (IsServiceExisted("WCFServiceMgr"))
{
string[] cmdline = { };
string serviceFileName = System.Reflection.Assembly.GetExecutingAssembly().Location; TransactedInstaller transactedInstaller = new TransactedInstaller();
AssemblyInstaller assemblyInstaller = new AssemblyInstaller(serviceFileName, cmdline);
transactedInstaller.Installers.Add(assemblyInstaller);
transactedInstaller.Uninstall(null);
}
}
catch (Exception )
{
}
}
}
#region 检查服务存在的存在性
/// <summary>
/// 检查服务存在的存在性
/// </summary>
/// <param name=" NameService ">服务名</param>
/// <returns>存在返回 true,否则返回 false;</returns>
public static bool IsServiceExisted(string NameService)
{
ServiceController[] services = ServiceController.GetServices();
foreach (ServiceController s in services)
{
if (s.ServiceName.ToLower() == NameService.ToLower())
{
return true;
}
}
return false;
}
#endregion
}
}

6,制作两个bat脚本,放在程序运行目录下,实现自动安装和卸载
install.bat

WinServiceHosting.exe -i
pause

uninstall.bat

WinServiceHosting.exe -u
pause

六,客户端

1,在服务器上执行install.bat安装并启动服务,按照服务地址,在项目中添加服务引用,自动生成服务代理类和配置文件

2,由于设定客户端免装证书,不进行服务器认证,配置文件如下:

 <system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService">
<security>
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://192.168.126.129:8733/" behaviorConfiguration="ClientWithoutCABehavior" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IService" contract="ServiceReference1.IService"
name="WSHttpBinding_IService">
<identity>
<!-dns要与服务器配置相同,如服务器无此配置,要与证书名相同-->
<dns value="WCfServerCA" />
</identity>
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name ="ClientWithoutCABehavior">
<!--客户端免装证书,所以不进行服务证书认证-->
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="None"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>

3,客户端测试调用服务代码:

using ConsoleClient.ServiceReference1;
using System;
namespace ConsoleClient
{
class Program
{
static void Main(string[] args)
{
using (ServiceClient proxy=new ServiceClient("WSHttpBinding_IService"))
{
//调用服务前写入用户名密码供服务器验证
string strUserName = "z";
proxy.ClientCredentials.UserName.UserName = strUserName;
proxy.ClientCredentials.UserName.Password = "";
//调用服务
string strMessage = proxy.Hello(strUserName);
Console.WriteLine(strMessage);
}
Console.WriteLine("press any key to continue。。。");
Console.ReadKey();
}
}
}

Over !

参考:

  消息安全模式之UserName客户端身份验证(参考:老徐的博客)

  WCF服务的Windows 服务程序寄宿

项目中使用WCF替换asmx Web service总结的更多相关文章

  1. WCF 、Web API 、 WCF REST 和 Web Service 的区别

    WCF .Web API . WCF REST 和 Web Service 的区别 The .Net framework has a number of technologies that allow ...

  2. WCF与ASMX Web服务差异比较[译]

    First of all, it needs to understand that WCF Service provides all the capabilities of .NET web serv ...

  3. 转 Difference between WCF and Web API and WCF REST and Web Service

    http://www.dotnet-tricks.com/Tutorial/webapi/JI2X050413-Difference-between-WCF-and-Web-API-and-WCF-R ...

  4. 用JQuery中的Ajax方法获取web service等后台程序中的方法

    用JQuery中的Ajax方法获取web service等后台程序中的方法 1.准备需要被前台html页面调用的web Service,这里我们就用ws来代替了,代码如下: using System; ...

  5. WCF、Web API、WCF REST、Web Service

    WCF.Web API.WCF REST.Web Service 区别 Web Service It is based on SOAP and return data in XML form. It ...

  6. WCF、Web API、WCF REST、Web Service的区别

    Difference between WCF and Web API and WCF REST and Web Service   The .Net framework has a number of ...

  7. 个人项目中的WCF使用

    今天闲着无事,给大家分享一下我的一个项目中WCF的使用.我这项目使用的是Silverlight,至于其他类型的使用方法也是一样的. 1.建立一个Silverlight带Web项目的解决方案. 2.在w ...

  8. 如何创建和发布.asmx Web Service

    创建和发布Web ServiceWeb服务方法中可以返回一个DataSet对象 WEB服务可以说是下一代WEB应用程序的基础,无论客户端是WINDOWS应用.ASP.NET Web Form程序.甚至 ...

  9. WCF、Web API、WCF REST、Web Service之区别

    http://www.dotnet-tricks.com/Tutorial/webapi/JI2X050413-Difference-between-WCF-and-Web-API-and-WCF-R ...

随机推荐

  1. Web自动化测试框架Watir(基于Ruby) - 第1章 Windows下安装与部署

    一.前言 Web自动化测试一直是一个比较迫切的问题,对于现在web开发的敏捷开发,却没有相对应的敏捷测试,故开此主题,一边研究,一边将Web自动化测试应用于工作中,进而形成能够独立成章的博文,希望能够 ...

  2. rabbitMQ应用,laravel生产广播消息,springboot消费消息

    最近做一个新需求,用户发布了动态,前台需要查询,为了用户读取信息响应速度更快(MySQL很难实现或者说实现起来很慢),所以在用户动态发布成功后,利用消息机制异步构建 redis缓存 和 elastic ...

  3. sysbench 0.5使用手册

    注意:本文刚开始只介绍了sysbench 0.5之前的版本,在了解了sysbench 0.5之后进行了补充,大部分测试和参数都是一样的,只是sysbench 0.5 在测试数据库方面更加全面丰富. 关 ...

  4. java之JMS

    一.简介:JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进 ...

  5. python + docker, 实现天气数据 从FTP获取以及持久化(三)-- python获取FTP数据

    前言 经过前面两个小节的介绍,我们已经完成了MySQL数据库的搭建和数据库操作的事宜. 在本小节中,我们需要完成的任务是:使用python从FTP服务其上面获取文本文件. 搭建测试FTP服务器 LZ的 ...

  6. hihocoder-1080题解

    一.题目链接 http://hihocoder.com/problemset/problem/1080 二.题意 一维区间,需要做区间增加和区间置值,以及对整个区间求和. 三.思路 显然线段树是个利器 ...

  7. id取模分表

    场景 1 假设按用户id分2个库 每个库分10张表. 分表策略 1.用户id%2 确定库  用户id%3确定表. 2.(用户id%(2*10))/ 10  取整确定库,(用户id%(2*10)%10确 ...

  8. python推荐书籍

    推荐的python电子书 python学习路线图 优先级 入门:python核心编程 提高:python cookbook 其他 (1).数据分析师 需要有深厚的数理统计基础,但是对程序开发能力不做要 ...

  9. Linux下安装MATLAB

    Why do I see "Preparing installation files ... Installing ... Finished..." in the terminal ...

  10. CImage 往Picture Control贴图 图像显示不正常

    在使用CImage 往vc控件 picture Control 上贴图的时候图像显示不太正常如图: 已知原始图片的宽高为640*640  而我上面picture Control  控件宽高小于原始图像 ...