以前项目解决方案中,用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. 【linux】mkdir -p命令

    如果要创建目录A并创建目录A的子目录B,没有用-p的情况下是mkdir 2次 如果用-p 可以直接创建2个目录 (迭代创建).mkdir -p 目录A/子目录B就可以

  2. MySql-5.7.17 -winx64的安装配置

    一.下载软件 1. 进入mysql官网,登陆自己的Oracle账号(没有账号的自己注册一个),下载Mysql-5.7.17,下载地址:http://dev.mysql.com/downloads/my ...

  3. 记一次 在 HP zbook G3 笔记本上安装Ubuntu16.04LTS 的 心(填)路(坑)旅程

    背景 同事MM申请的新笔记本暂时没有用,问我需不需要用. 本着 “宇宙都是xx的”(厚颜无耻~~)思想就接受了. 拿到本本一看,HP zbook G3, 配置还不错(500G SSD, 16G mem ...

  4. 补充: istio安装

    首先有一个概念: CRD - Custom Resource Definitions: CRDS文件: install/kubernetes/helm/istio/templates/crds.yml ...

  5. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #13 使用Block I/O控制器设置I/O优先级

    HACK #13 使用Block I/O控制器设置I/O优先级 本节介绍使用Block I/O控制器的功能设置I/O优先级的方法.Block I/O控制器可以将任意进程分组,并对该分组设置I/O的优先 ...

  6. Spring batch学习 (1)

    Spring Batch 批处理框架 埃森哲和Spring Source研发 主要解决批处理数据的问题,包含并行处理,事务处理机制等.具有健壮性 可扩展,和自带的监控功能,并且支持断点和重发.让程序员 ...

  7. android studio 3.0.1使用笔记(二)

    发布前如何生成正式签名的APK? 一,查看APK签名方法:??Preferences->Android->Build可以查看到这个默认keystore文件的位置. 二,新建正式签名过程: ...

  8. NodeJs通过async/await处理异步

    ##场景 远古时代 我们在编写express后台,经常要有许多异步IO的处理.在远古时代,我们都是用chunk函数处理,也就是我们最熟悉的那种默认第一个参数是error的函数.我们来模拟一个Mongo ...

  9. ICaptureGraphBuilder2::RenderStream 智能连接方法浅析

    ICaptureGraphBuilder2::RenderStream HRESULT RenderStream( [in] const GUID *pCategory, [in] const GUI ...

  10. 前端开发之jQuery属性和文档操作

    主要内容: 1.jQuery属性操作 2.jQuery文档操作 一.jQuery属性操作 1.什么是jQuery的属性操作? jQuery的属性操作模块包括四个部分:html属性操作,dom属性操作, ...