关于使用UserName方式进行认证的wcf服务
前几天读wcf的高级编程时觉得自己虽然以前写过WCF的接口服务,但是都是关于主要的实现代码并没有考虑类似安全机制的东西,想想通过各种调用别人的API的接口的时候,都会有安全认证(这不是废话吗,没有认证怎么挣钱?),正好也读到这部分的内容,所以决定试试,记录一下过程防止自己长时间不用,又忘记。下面开始分析和实验。
开始之前事先声明:本次采用的是基于WCF自带的和windows系统自带的X509证书认证的,会涉及到系统自动创建数据库和数据表。
1、读过安全方面的内容后知道WCF自带的有关于基于用户的安全登录验证的实现。
2、可以选择基于那种安全模式的认证,security 节点mode的枚举值有none,Windows,username,等。后文可在配置文件中找到。*
表示这块也不怎么理解先记着。
*
3、本文基于安全的模式中的username进行认证。
正文开始
WCF的客户端和服务端都正常编写一个最基本的用于测试的接口和方法。客户端先不实现。
过程:用户调用service,服务端验证用户传来的用户名和密码(传输过程用X509证书验证及加解密),验证通过则给予调用服务。
1、因为是username模式,所以需要创建用户名等,我们在WCF宿主上来进行编码,如下,注意需要引用System.Web.Security命名空间
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
if (Membership.FindUsersByName("*******").Count == 0)
{
//会在数据库中创建一个用户名和密码还有邮箱的一条记录
Membership.CreateUser("*******", "*Password01", "xuyue1001@hotmail.com");
}
using (ServiceHost host = new ServiceHost(typeof(WcfService1.Service1)))
{
#region 这段代码可以让app.config进行配置得来 注意:如果是控制台寄宿单独的程序需要配置其服务的行为,ServiceMetadataBehavior
//host.AddServiceEndpoint(typeof(WcfService1.IService1), new BasicHttpBinding(), new Uri("http://localhost:5443/Service1"));
//ServiceMetadataBehavior behavior = new ServiceMetadataBehavior
//{
// HttpGetEnabled = true,
// HttpGetUrl = new Uri("http://localhost:5443/Service1/metadata")
//};
//ServiceMetadataBehavior hh = new ServiceMetadataBehavior();
//host.Description.Behaviors.Add(behavior);
#endregion
if(host.State!=CommunicationState.Opening)
{
host.Open();
Console.Write("服务已经启动");
Console.ReadLine();
}
else
{
host.Close();
}
}
}
}
}
到这里Server端和宿主的代码基本上完成了,接下来就是如何让这些代码能够协同工作,那就需要修改我们的配置文件Web.config,有3个地方需要修改或者添加。
<connectionStrings>
<add name="AspNetDb" connectionString="Data Source=.;Initial Catalog=testdb;Integrated Security=True"/>
</connectionStrings>
<system.web>
<!--1、会员身份也就是认证的默认程序-->
<membership defaultProvider="myProvider">
<providers>
<!--1、主要的配置是关于sqlserver的就是默认提供关于会员的信息的程序,没错是sqlserver数据库前面说过不用我们自己动手创建数据库只要设置好连接,程序会自动创建-->
<add name="myProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="AspNetDb" applicationName="MembershipAuthenticationDemo"
requiresQuestionAndAnswer="false"/>
</providers>
</membership>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="userNameCredentialBinding">
<!--2、设置采取的安全认证模式 消息模式-->
<security mode="Message">
<!--消息模式中的用户名认证-->
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<!-- 注意: 服务名称必须与服务实现的配置名称相匹配。 --><!--行为配置属性是后面的行为中的配置-->
<service name="WcfService1.Service1" behaviorConfiguration="behaviorConfiguration" >
<endpoint address="http://localhost:5443/Service1" binding="wsHttpBinding" bindingConfiguration="userNameCredentialBinding"
contract="WcfService1.IService1"></endpoint>
</service>
</services>
<behaviors>
<!--服务的行为配置 -->
<serviceBehaviors>
<behavior name="behaviorConfiguration" >
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:5443/"/>
<!--3、以下是重点 对username模式的认证配置-->
<serviceCredentials>
<!--其中 findValue 寻找的证书名称,storeLocation 保存位置 storeName 就是具体的文件夹MY代表个人 x509FindType 查找的类型(以名称)-->
<serviceCertificate storeLocation="CurrentUser" storeName ="My" x509FindType="FindBySubjectName" findValue="MyServerCert"/>
<!--userNamePasswordValidationMode=用户名密码验证模式为成员资格提供者 membershipProviderName 成员资格提供者名称=membership节点的值-->
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="myProvider"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
此时,我们还没有证书,接下来我们制作证书,用到windows提供的makecert.exe命令工具生成证书
1、点击windows标志找到vs版本号文件夹点击tools文件夹选择兼容命令工具以管理员身份运行赋值下面命令回车执行makecert.exe -sr LocalMachine -ss My -n CN=MyServerCert -sky exchange –pe
-sr CurrentUser:指定主题的证书存储位置。Location 可以是 currentuser(当前用户)(默认值)或 localmachine(本地计算机)
-ss MyTestContainer:指定主题的证书存储名称,输出证书即存储在那里。
-n CN=TestCert:指定主题的证书名称。此名称必须符合 X.500 标准。最简单的方法是在双引号中指定此名称,并加上前缀 CN=;例如,”CN=myName”。
-sky exchange:指定颁发者的密钥类型,必须是 signature、exchange 或一个表示提供程序类型的整数。默认情况下,可传入 1 表示交换密钥,传入 2 表示签名密钥。
-pe:将所生成的私钥标记为可导出。这样可将私钥包括在证书中。
Cmd->certmgr.msc回车在控制台中选择个人查看生成的证书,如果保存在本地计算机需要运行->MMC
在控制台菜单,文件→添加/删除管理单元→添加按钮→选”证书”→添加→选”我的用户账户”→关闭→确定
在控制台菜单,文件→添加/删除管理单元→添加按钮→选”证书”→添加→选”计算机账户”→关闭→确定
然后在计算机下查看证书
找到生成好的证书,我们要清楚在配置文件中我们规定的证书查找位置如果配置的是在本地计算机,那么确保证书保存在本地计算机,如果是当前用户,那么应该在个人文件夹下。
因为客户端要通过证书加密调用服务端,所以证书必须导出并发送到客户端(导出时连私匙一起导出),在客户端将证书导入到受信任的列表中
证书的导出导入如下所示:
1、选择指定的证书右击导出
2、设置密码
3、指定导出文件保存的名称和位置
4、完成
导入
1、选择好需要导入的目标文件列表(本例是要导入到受信任的证书列表)右键单击导入选择需要导入的证书点击下一步
2、填写密码点击下一步
完成导出和导入具体可以参考
https://blog.csdn.net/whwqs/article/details/8239379(包括添加权限)
最后是客户端的调用
像普通的客户端一样去进行服务引用的添加如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApplication4.ServiceReference1;
using System.Web.Security;
using System.ServiceModel;
using System.ServiceModel.Security;
namespace ConsoleApplication4 //通过username安全认证调用wcf服务案列,需要导入证书,和必要的用户名和密码 配置文件是重点
{
class Program
{
static void Main(string[] args)
{
Program pp = new Program();
Console.Write(pp.start(1));
Console.ReadKey();
}
public bool start(int i)
{
//服务实例化
Service1Client client = new Service1Client();
//表示当前的 UserName凭据
UserNamePasswordClientCredential cre = client.ClientCredentials.UserName;
//凭据的用户名和密码
cre.UserName = "*********";
cre.Password = "*Password01";
//因为后期动过服务宿主并重新应用过服务,可能客户端的证书出现了变动
return client.get(i);
}
}
}
正如文中所属配置文件比较重要在system.serviceMode节点下添加如下代码段
<!--以下节点是后增的-->
<behaviors>
<endpointBehaviors>
<behavior name="peerTrustSvcCertValidation"><!--是上面节点endpoint中behaviorConfiguration属性的值-->
<clientCredentials>
<serviceCertificate>
<!--一个列出验证证书方法的枚举。-->
<authentication certificateValidationMode="PeerTrust"/><!--证书验证模式 如果证书位于被信任的人的存储区中,则有效。-->
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
至此完成,进行测试只要用户名正确和密码正确是可以调用服务的。