以前项目解决方案中,用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. 《快学Scala》

    Robert Peng's Blog - https://mr-dai.github.io/ <快学Scala>Intro与第1章 - https://mr-dai.github.io/S ...

  2. CentOS下用yum配置php+mysql+apache

    环境: CentOS5.4  yum 想在一台CentOS的机器上安装配置支持dedeCMS的php+mysql+apache环境,把摸索的过程记录如下: 1. 安装Apahce, PHP, Mysq ...

  3. HBuilder webApp开发 Websql增删改查操作

    来源:http://blog.csdn.net/zhuming3834/article/details/51471434 这段时间公司要求我们做原生iOS和安卓的都转做H5开发APP,使用的工具HBu ...

  4. MariaDB Galera Cluster的配置测试

    参考的https://fykuan.hsnuer.net/blog/2015/01/23/debian-%E4%B8%8A%E5%AE%89%E8%A3%9D-mariadb-galera-clust ...

  5. Visual Studio Team Foundation Server 2013

    下载地址:http://www.microsoft.com/zh-cn/download/details.aspx?id=42308 Microsoft Visual Studio Team Foun ...

  6. vue打包优化

    网站首页第一次加载很慢,优化过后从十多二十秒缩短到了几秒,主要是打包的时候按需加载了,然后使用了gzip压缩. 这是优化之前的 发现vendor特别大,所有引用的第三方库都会打到这个包里面;另外就是之 ...

  7. ThinkJava-标准IO

    1 从标准输入中读取 按照标准1/0模型, Java提供了System.in.System.out和System.err.在整本书里,我们已经 看到了怎样用System.out将数据写出到标准输出,其 ...

  8. mac下执行 appium-doctor 出现 “Could not detect Mac OS X Version from sw_vers output: '10.12 '” 解决方法

      You can config file by this command: grep -rl "Could not detect Mac OS X Version from sw_vers ...

  9. mongodb与SQL常见语句对照

    inert into users value(3,5) db.users.insert({a:3,b:5})     select a,b from users db.users.find({}, { ...

  10. ES6系列_16之模块化操作

    ES6的模块化操作主要包括两个方面. (1)export :负责进行模块化,也是模块的输出. (2)import : 负责把模块引,也是模块的引入操作. export的用法: export可以让我们把 ...