接触WCF时间比较短,在项目中要使用X509证书,纠结好几天终于有了结论,因此为了方便日后查阅和园友交流特意单独将部分代码提出,并做以记录。

1.准备工作

制作X509证书,此处用到三个证书名称

导入证书步骤:

第一步:运行mmc 打开控制台,添加证书

将证书导入,再设置证书的读取权限

2.方便阅读下文,先展示下代码整体结构

3.编写服务代码(WcfSite工程下)

服务一:

 public class HelloService : IHelloService
{
public string DoWork()
{
return "Hello";
}
} [ServiceContract]
public interface IHelloService
{
[OperationContract]
string DoWork();
}

服务二:

 public class HiService : IHiService
{
public string DoWork()
{
return "Hi";
}
} [ServiceContract]
public interface IHiService
{
[OperationContract]
string DoWork();
}

服务三:

public class NiHaoService : INiHaoService
{
public string DoWork()
{
return "你好";
}
} [ServiceContract]
public interface INiHaoService
{
[OperationContract]
string DoWork();
}

服务写完之后开始着手准备X509的自定义验证

此实例的验证逻辑如下:

第一步:验证客户端x509证书的有效性;

当客户端试图调用服务资源时,首先进入此处进行x509的验证

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IdentityModel.Selectors;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.ServiceModel; namespace WcfSite.Code
{
/// <summary>
/// 验证X509证书
/// </summary>
public class X509Validator : System.IdentityModel.Selectors.X509CertificateValidator
{
/// <summary>
/// 日志路径
/// </summary>
private const string logPath = @"\Safety";
/// <summary>
/// CA序列号集合Key=CA,Value=SN
/// </summary>
private static Dictionary<string, string> SNList = new Dictionary<string, string>();
private static Object objlock = new object(); #region X509CertificateValidator重写
/// <summary>
/// 验证X509证书
/// </summary>
/// <param name="certificate"></param>
public override void Validate(X509Certificate2 certificate)
{
if (certificate == null)
{
throw new FaultException("请安装X509证书");
}
if (SNList.Count == )
{
GetX509SerialNumberList();
}
lock (objlock)
{
if (!SNList.ContainsKey(certificate.Subject) || !SNList.ContainsValue(certificate.SerialNumber.ToUpper()))
{
throw new FaultException("X509证书无效");
}
}
} #endregion #region 私有方法
/// <summary>
/// 获取x509证书序列号
/// </summary>
private void GetX509SerialNumberList()
{
XmlDocument doc = new XmlDocument();
doc.Load(AppDomain.CurrentDomain.BaseDirectory + "X509SerialNumbers.xml");
XmlNodeList nodes = doc.SelectNodes("X509SN/SerialNumbers/Number");
foreach (XmlNode node in nodes)
{
string ca = String.Empty;//CA名称
string sn = String.Empty;//CA序列号
foreach (XmlAttribute xa in node.Attributes)//校验用户名密码
{
if (xa.Name == "CA")
ca = xa.Value;
else if (xa.Name == "SN")
sn = xa.Value;
}
if (!String.IsNullOrEmpty(ca) && !String.IsNullOrEmpty(sn))
SNList.Add(ca, sn.ToUpper());
}
}
#endregion
}
}

第二步:验证客户端是否有权限调用将要调用的服务资源

验证客户端使用的x509证书是否有权限调用服务资源

 /// <summary>
/// 提供对服务操作的授权访问检查
/// </summary>
public class CustomServiceAuthorizationManager : System.ServiceModel.ServiceAuthorizationManager
{
/// <summary>
/// 日志路径
/// </summary>
private const string logPath = @"\Safety"; #region ServiceAuthorizationManager重写 /// <summary>
/// 检查授权
/// </summary>
/// <param name="operationContext"></param>
/// <returns></returns>
protected override bool CheckAccessCore(OperationContext operationContext)
{
//请求调用的资源url
string action = operationContext.RequestContext.RequestMessage.Headers.Action;
Console.ForegroundColor = ConsoleColor.Red;
Console.ForegroundColor = ConsoleColor.White;
//ClaimSet 表示与某个实体关联的声明的集合。
//获取与授权策略关联的声明集
foreach (System.IdentityModel.Claims.ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
{
if (cs.Issuer == System.IdentityModel.Claims.ClaimSet.System)
{
foreach (System.IdentityModel.Claims.Claim claim in cs.FindClaims("http://tempuri.org/", System.IdentityModel.Claims.Rights.PossessProperty))
{
//校验是否有调用权限
if (claim.Resource.ToString() == action)
{
return true;//通过
}
else
{
string url = action.Substring(, action.LastIndexOf('/'));
if (claim.Resource.ToString() == url + "/all")//可以调用该服务下所有的方法
return true;
} }
}
}
return false;//不通过
} #endregion
}

获取服务端定义的资源权限集合

 /// <summary>
/// 查询用户可调用的资源
/// 定义一组用于对用户进行授权的规则
/// </summary>
public class CustomAuthorizationPolicy : System.IdentityModel.Policy.IAuthorizationPolicy
{
/// <summary>
/// 证书名称(角色)-资源集合
/// </summary>
private static Dictionary<string, List<string>> dicRoleResources = new Dictionary<string, List<string>>();
private static Object objlock = new object();
/// <summary>
/// 日志路径
/// </summary>
private const string logPath = @"\Safety";
string id = string.Empty;
public CustomAuthorizationPolicy()
{
id = new Guid().ToString();
}
public System.IdentityModel.Claims.ClaimSet Issuer
{
get { return System.IdentityModel.Claims.ClaimSet.System; }
}
public string Id
{
get { return id; }
} #region IAuthorizationPolicy方法实现 /// <summary>
/// 查询用户可调用的资源
/// </summary>
/// <param name="evaluationContext"></param>
/// <param name="state"></param>
/// <returns></returns>
public bool Evaluate(System.IdentityModel.Policy.EvaluationContext evaluationContext, ref object state)
{
bool flag = false;
bool r_state = false;
if (state == null) { state = r_state; } else { r_state = Convert.ToBoolean(state); }
if (!r_state)
{
List<System.IdentityModel.Claims.Claim> claims = new List<System.IdentityModel.Claims.Claim>();
foreach (System.IdentityModel.Claims.ClaimSet cs in evaluationContext.ClaimSets)
{
foreach (System.IdentityModel.Claims.Claim claim in cs.FindClaims
(System.IdentityModel.Claims.ClaimTypes.Name, System.IdentityModel.Claims.Rights.PossessProperty))
{
IEnumerable<string> resourceList = HelpGetServiceResourceByName(claim.Resource.ToString());
foreach (string str in resourceList)
{
//授权的资源
claims.Add(new System.IdentityModel.Claims.Claim("http://tempuri.org/", str, System.IdentityModel.Claims.Rights.PossessProperty));
}
}
}
evaluationContext.AddClaimSet(this, new System.IdentityModel.Claims.DefaultClaimSet(Issuer, claims)); r_state = true; flag = true;
}
else
{
flag = true;
}
return flag;
} #endregion #region 私有方法 /// <summary>
/// 通过证书名称(角色)获取资源
/// </summary>
/// <param name="caRole">证书名称(角色)</param>
/// <returns></returns>
private IEnumerable<string> HelpGetServiceResourceByName(string caRole)
{
if (dicRoleResources.Count == )
{
lock (objlock)
{
GetRoleResourceList();
}
}
return dicRoleResources[caRole];
}
/// <summary>
/// 读取所有证书名称(角色)的可访问资源
/// </summary>
private void GetRoleResourceList()
{
XmlDocument doc = new XmlDocument();
doc.Load(AppDomain.CurrentDomain.BaseDirectory + "RoleResourceConfig.xml");
XmlNodeList nodes = doc.SelectNodes("ResourceConfig/Role");
foreach (XmlNode node in nodes)
{
foreach (XmlAttribute xa in node.Attributes)
{
if (xa.Name == "Name") //查询角色下的所有资源
{
List<string> lists = new List<string>(); //资源集合
foreach (XmlNode xn in node.ChildNodes)
{
if (xn.Name == "Resource")
{
lists.Add(xn.InnerXml);
}
}
if (!dicRoleResources.ContainsKey(xa.Value))
dicRoleResources.Add(xa.Value, lists);
}
}
}
}
#endregion }

到此,所有服务方法和x509自定义验证代码都已完成。之后将要在配置文件中配置客户端x509的权限信息

首先配置客户端x509证书的序列号,因为此实例是通过序列号验证证书是否有效的(X509SerialNumbers.xml)

 <?xml version="1.0" encoding="utf-8" ?>
<X509SN>
<SerialNumbers>
<Number CA="CN=Test01CA" SN="0e9a8c9d238597ae47ef56eb7e3a0b61"/>
</SerialNumbers>
</X509SN>

然后配置客户端x509证书能访问的服务资源权限(RoleResourceConfig.xml)

 <?xml version="1.0" encoding="utf-8" ?>
<ResourceConfig>
<!--TEST1-->
<Role Name="Test01CA">
<!--格式:地址+方法名;all表示有权限访问该地址下所有的服务方法-->
<Resource>http://tempuri.org/IHiService/all</Resource>
<!--格式:地址+方法名;all表示有权限访问该地址下所有的服务方法-->
<Resource>http://tempuri.org/IHelloService/all</Resource>
</Role>
<!--TEST2-->
<Role Name="Test02CA">
<!--格式:地址+方法名;all表示有权限访问该地址下所有的服务方法-->
<Resource>http://tempuri.org/INiHaoService/all</Resource>
</Role>
</ResourceConfig>

最后一项,配置服务传说中的ABC

直接上web.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="WcfSite.HiService" behaviorConfiguration="httpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsBinding" contract="WcfSite.IHiService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9903/HiService" />
</baseAddresses>
</host>
</service>
<service name="WcfSite.HelloService" behaviorConfiguration="httpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsBinding" contract="WcfSite.IHelloService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9903/HelloService" />
</baseAddresses>
</host>
</service>
<service name="WcfSite.NiHaoService" behaviorConfiguration="httpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsBinding" contract="WcfSite.INiHaoService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9903/NiHaoService" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="wsBinding" closeTimeout="00:10:00" openTimeout="00:10:00"
receiveTimeout="01:00:00" sendTimeout="01:00:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="" maxReceivedMessageSize=""
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true">
<readerQuotas maxDepth="" maxStringContentLength="" maxArrayLength=""
maxBytesPerRead="" maxNameTableCharCount="" />
<reliableSession inactivityTimeout="01:00:00"/>
<security mode="Message">
<!--定义消息级安全性要求的类型,为证书-->
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="httpBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceCredentials>
<serviceCertificate findValue="TestServiceCA" x509FindType="FindBySubjectName"
storeLocation="LocalMachine" storeName="My" />
<clientCertificate>
<!--自定义对客户端进行证书认证方式 这里为 None-->
<authentication certificateValidationMode="Custom"
customCertificateValidatorType="WcfSite.Code.X509Validator,WcfSite"/>
</clientCertificate>
</serviceCredentials>
<serviceAuthorization serviceAuthorizationManagerType="WcfSite.Code.CustomServiceAuthorizationManager,WcfSite">
<authorizationPolicies>
<add policyType="WcfSite.Code.CustomAuthorizationPolicy,WcfSite"/>
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>

到此编写服务端代码完成。

继续客户端的,比较简单:

直接引用服务,修改一下配置

先上客户端config代码

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsBinding_Test" closeTimeout="00:10:00" openTimeout="00:10:00"
receiveTimeout="01:00:00" sendTimeout="01:00:00" allowCookies="false" bypassProxyOnLocal="false"
hostNameComparisonMode="StrongWildcard" maxBufferPoolSize=""
maxReceivedMessageSize="" messageEncoding="Text" textEncoding="utf-8"
useDefaultWebProxy="true">
<readerQuotas maxDepth="" maxStringContentLength="" maxArrayLength=""
maxBytesPerRead="" maxNameTableCharCount="" />
<reliableSession inactivityTimeout="01:00:00"/>
<security>
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="myClientBehavior">
<clientCredentials>
<!--客户端证书-->
<clientCertificate findValue="Test01CA" storeName="My" storeLocation="LocalMachine"
x509FindType="FindBySubjectName"/>
<serviceCertificate>
<authentication certificateValidationMode="None"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://localhost:9903/HiService.svc" binding="wsHttpBinding" behaviorConfiguration="myClientBehavior"
bindingConfiguration="wsBinding_Test" contract="_HiService.IHiService"
name="WSHttpBinding_IHiService">
<identity>
<dns value="TestServiceCA" />
</identity>
</endpoint>
<endpoint address="http://localhost:9903/NiHaoService.svc" binding="wsHttpBinding" behaviorConfiguration="myClientBehavior"
bindingConfiguration="wsBinding_Test" contract="_NiHaoService.INiHaoService"
name="WSHttpBinding_INiHaoService">
<identity>
<dns value="TestServiceCA" />
</identity>
</endpoint>
<endpoint address="http://localhost:9903/HelloService.svc" binding="wsHttpBinding" behaviorConfiguration="myClientBehavior"
bindingConfiguration="wsBinding_Test" contract="_HelloService.IHelloService"
name="WSHttpBinding_IHelloService">
<identity>
<dns value="TestServiceCA" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
  class Program
{
static void Main(string[] args)
{
try
{
_HelloService.HelloServiceClient hs = new _HelloService.HelloServiceClient();
Console.WriteLine("HelloService:" + hs.DoWork());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
_HiService.HiServiceClient hs = new _HiService.HiServiceClient();
Console.WriteLine("HiService:" + hs.DoWork());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
_NiHaoService.NiHaoServiceClient hs = new _NiHaoService.NiHaoServiceClient();
Console.WriteLine("NiHaoService:" + hs.DoWork());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}

到这里 客户端和服务端的代码都算撸完了。

现在进入运行阶段

1.先将服务端启动,再启动客户端

2.出结果

。。。。。

源码下载 Wcfx509.rar

最新文章

  1. JAVA 设计模式 享元模式
  2. kill 根据PID终止进程
  3. [poj2184]我是来水一下背包的
  4. Minimum Size Subarray Sum
  5. phpcms筛选功能
  6. linux mysql服务器迁移
  7. Unity帧序列实时渲染脚本
  8. (转)html5开发之viewport使用
  9. Apache CXF 101 Win Eclipse开发环境搭建
  10. python打包成exe
  11. 兼容 console 没删除引起 低级浏览器 报错问题
  12. 原生jdbc操作mysql数据库详解
  13. iOS开发基础-图片切换(3)之属性列表
  14. Tkinter 项目-屏保
  15. 简单的proxy之TinyHTTPProxy.py
  16. Shell教程 之流程控制
  17. (转)python中的selectors模块
  18. 显示eclipse中Problem窗口的方法
  19. jQuery基础笔记(5)
  20. psutil-3.4.2才是我的老系统(Windows XP)的菜

热门文章

  1. C# ---sender
  2. String创建方式的区别
  3. ()IT 职场经验)一位10年Java工作经验的架构师的经验分享,感觉很受用。
  4. .NET中如何测试Private和Protected方法
  5. Spring -- 入门,装备集合,自动装配,分散装配,自定义编辑器
  6. java8 函数接口 Predicate例子
  7. Spark- SparkSQL中 Row.getLong 出现NullPointerException错误的处理方法
  8. 初试Orchard Core CMS
  9. Django进阶Model篇003 - 数据库同步技巧
  10. mysql数据库(三):查询的其他用法