以前项目解决方案中,用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. 正则,以“this.Name”开头,以“;”结尾

    string regex="this\\.Name(.*?);"; string regex="this\\.Name(.*?);"; 以size开头,以数字结 ...

  2. java之mapstruct的应用

    一.MapStruct是一个代码生成器,简化了不同的Java Bean之间映射的处理,所以映射指的就是从一个实体变化成一个实体.例如我们在实际开发中,DAO层的实体和一些数据传输对象(DTO),大部分 ...

  3. servlet 3.0无需配置web.xml,使用注入方式配置servlet实现登陆功能(服务器需要支持servlet3.0)

    首先申明上面的报错红叉,我也不知道怎么回事.总之能运行. 新建项目时选择java EE6.0,低版本没有servlet3.0. 先看一个基本示例. Test.java是用来测试无需配置文件,无需静态页 ...

  4. redis for windows之nosql还需要入门?

    RT:nosql给很多人的感觉就是上手特别快,本文也是一篇入门级的,哎~~ 又写了一篇没有卵用的博文,姑且把它当作一篇文档吧,还是没有发布/订阅.事件...等介绍的一篇残缺文档,幸好废话不是很多,凑合 ...

  5. 给iOS开发新手送点福利,简述UIActivityIndicatorView的属性和用法

    UIActivityIndicatorView 1.   activityIndicatorViewStyle 设置指示器的样式 UIActivityIndicatorViewStyleWhiteLa ...

  6. 数据结构和算法之栈和队列三:自定义一个栈包含min函数

    我们都知道一个栈的特点是后进先出,如果我们要实现在O(1)的时间内找到一个栈里面的最小值,我们应该怎么解决?如果我们采用遍历获取的思路那必然所需要的时间是O(N)与我们所需要的要求明显不符合,这时候我 ...

  7. django-DIL模板自定义过滤器,自定义标签,自定义包含标签

    自定义过滤器 DTL模板语言生来只是为了方便的展示信息,所以与编程语言相比显得有点薄弱,有时候不能满足我们的需求.因此django提供了一个接口,让开发者能自定义标签和过滤器. 首先,你需要添加一个t ...

  8. Spring MVC 学习 之 - 配置简单demo

    1.环境参数: Maven:3.1.1 JDK  :1.6 2.项目文件结构图: 3.各文件配置: 3.1. pom.xml <project xmlns="http://maven. ...

  9. oreilly 用户故事地图

    这本书是完全买亏了,一点作用也没有. 整篇有用的字很少,还花了我¥16,总结如下: 用户故事模板: 作为用户角色(who),我想要某项功能(what),这样我可以 XXX(原因,why)

  10. curl返回302 found问题相关

    在使用curl 的时候 ,偶尔会遇到一些URL跳转到新的URL,即HTTP中的3XX(redirection,重定向 ) 此时如果不设置自动跳转倒新url的话,可能会出现问题. 一些网上的解决方法: ...