以前项目解决方案中,用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. android adb端口被占用解决方法

    1.输入adb devices命令 C:\Users\Nick>adb devices List of devices attached adb server version (31) does ...

  2. 【python】 Windows下pip安装包报错:Microsoft Visual C++ 9.0 is required Unable to find vcvarsall.bat

    刚在机器上windows环境下装上pip方便以后安装包的时候使用,谁知道第一次使用pip安装asyncio的时候就报错. 在Windows7x64下使用pip安装包的时候提示报错:Microsoft ...

  3. 真实赛车3,FERRARI之魂不买FERRARI 599 GTO可以解锁顶点系列。

    难点1,在仅有458 SPIDER的情况下,“TURBO BURST技巧混战”中 Mount Panorama速度快照,比较难.多重试十几次. 难点2,“TURBO BURST大满贯”中直道赛,用45 ...

  4. Excel VBA 找出选定范围不重复值和重复值

    Sub 找出选定范围内不重复的值() On Error Resume Next Dim d As Object Set d = CreateObject("scripting.diction ...

  5. bzoj 3965: [WF2011]Pyramids

    Description 如果你有足够的石块,那么建一座金字塔绝不算难事.举个例子,在一块平地上,我们铺一个10*10的矩形,然后在10*10的矩形上面铺一个9*9的,然后8*8的……以此类推,直到顶上 ...

  6. [java]经验集

    Calendar c = Calendar.getInstance(); c.set(1999,12,21); SimpleDateFormat sdf = new SimpleDateFormat( ...

  7. linux Valgrind使用说明-内存泄漏

    Valgrind使用说明 Valgrind manual: http://valgrind.org/docs/manual/manual.html valgrind的介绍.安装和使用 valgrind ...

  8. 1021 docker prometheus监控体系

    jmeter plugin监控的信息很少,只有cpu.内存.网络IO,但这些是不够的.例如对于分析mysql数据库的慢查询.最大连接数等更加细密度的信息. 服务端稳定测试的三个前提: 1.应用级别的自 ...

  9. 01:初识Redis

    付磊和张益军两位大咖写的葵花宝典(Redis开发和运维)学习笔记. 一.初识Redis 1.redis简介 Redis是一种基于键值对(key-value)的NoSQL数据库,与很多键值对数据库不同的 ...

  10. Creating an Android Project(创建一个android项目)

    一个android项目包含了你的应用程序中的所有源代码文件,我们可以通过android sdk tools轻松地创建一个拥有默认文件跟文件夹的android项目. 这部分课程我们将展示两种创建andr ...