客户端与服务之间的安全交互涉及多个方面,因为在传统的C/S和面向组件的应用程序里,服务需要验证调用者(身份验证),并且在执行敏感的操作之前授权,此外,除了技术问题,在分布式环境里加密服务的时候,还需要保护消息(传输安全),一旦消息安全到达,并且身份验证和授权通过,服务就可以根据标识来决定执行什么操作(身份标识管理)。下面讲解wcf安全中的一些经典问题:身分验证-授权-传输安全-身份标识管理。
一、身份验证:Authentication 验证是服务队调用者身份真伪的鉴别过程,对于调用者的验证是理所当然的,但是从客户端来说也需要验证服务的身份,也就是说,要确保调用的服务是真正的调用者调用的。有以下六种身份验证机制:
(1)None:表示服务不需要验证它的调用者,而且实际上任何调用者都可以调用服务。客户端为匿名客户端。在这种情况下,每个客户端拥有一个自己的证书,比如身份证。服务会使用证书来确保服务客户端的标识。我们经常使用HTTPS 访问网站,比如登陆一些安全级别较高的网站情况类似,或者使用网上银行时候,你的客户端证书就会起到鉴别客户端的作用。 (2)Windows:包含NTLM验证和Kerberos验证两种方式,需要Windows AD支持。一般使用在企业局域网内部。客户端和服务都会使用 Windows 帐户进行身份验证。Windows Communication Foundation 将会就 Kerberos 或 NTLM 进行协商,如果存在域,则优先选择 Kerberos(NTLM 实际上不会向客户端验证服务的身份,而只会向服务验证客户端的身份)。如果您想要使用 Kerberos,则必须让客户端根据配置中的服务主体名称验证服务的身份。如果您要在域环境中为客户端构建服务,您应明确地为其提供发送 Windows 账号的选项。
(3)UserNamePassword:也叫Basic 认证,客户端将提供用户名和密码。在这种情况下,服务会使用证书向客户端验证其标识。另外就是证书还将用加密客户端的用户名和密码,保证消息在传输过程中的安全。这个方式也是常见的加密方式。
(4)X.509 Certificate:服务将具有一个证书(客户端的公钥),客户端也具有一个其自己的证书(服务端的公钥)。当客户端向服务端发送消息时,使用证书加密消息,服务端使用私钥解密。反之亦然。证书就是包含公钥,证书标识,主题,指纹,签名算法等的一个文件。 (5)IssuedToken:安全令牌的概念在WSE3.0里曾经涉及到。它允许您的服务从安全性令牌服务 (STS) 接受一组签名的声明。因为它可以启用联合标识方案和 InfoCard。当您与某个合作伙伴组织联合时,您将允许该合作伙伴通过任何合适的技术对其自己的用户进行身份验证。在最理想的情况下,这将允许该合作伙伴组织中的用户通过单一登录使用您的服务,即便他们并不与您使用同一个 Active Directory 域,或不受您的信任。该合作伙伴组织中的用户需要使用 STS 进行身份验证,而 STS 可以发出一个签名的安全声明标记语言 (Security Assertion Markup Language, SAML) 令牌。您既可以直接接受该令牌,也可以要求将该令牌呈送给您组织中的 STS,以便让其评估该合作伙伴的声明,并发出第二个您可以使用的 SAML 令牌。理解起来有点复杂。实际也是一个标识,鉴别客户端的一个标识。就是一种更加灵活的身份验证方式。 好比你现在使用中国护照,有一天突然联合国实现了一种新的护照,全球统一护照,你可以进入任何一个国家,即使你在中国办理,但是其他国家可以再你落地的时候验证你的护照的有效性。然后告诉其他国家,共享着这次验证的结果。你的护照就是令牌。需要后续鉴别的身份证明。Issued这个单词的作用就在这里。需要鉴别的令牌。 UserNamePassword方式容易实现,但是在WCF框架下需要使用服务证书,这个是相对WSE3.0改变的地方。如果结合证书使用的话,会使的这种方式适合在Internet中使用。安全性较高。适合对发布到Internet的WCF服务常见的身份验证方式。X.509证书验证方式相对严谨,要求客户端提供有效的证书凭证,也就是每个客户端都要维护一个自己的证书,调用服务前,通过SOAP消息传递到WCF服务,WCF进行身份验证。这个需要CA支持。或者需要申请第三方商业证书。 定制方式也比较常见,用户根据需要定制自己的身份验证机制,如指纹,基因等技术。来代替现有的身份验证方式。
二、授权:Authorization
授权与调用者的工作内容密切相关,授权是在假设调用者身份真是的前提下进行的,没有验证,授权就毫无意义。对于授权,服务需要以来于某些特定的凭据库,这样客户端可以映射为本地的角色。当授权一个操作时,操作会声明或显示地要求某些特定的角色才可以访问,服务需要查询调用者的角色,或者从凭据库里查找后验证调用者属于特定的请求角色。更强大的是wcf支持两种凭据:使用Windows组(和账号)或ASP.NET Provider(如SQL SERVER Provider)存储和验证客户端程序凭据,wcf同样支持角色存储库,但是实现自定义凭据库最容易的方式就是自定义ASP.NET Provider.
三、传输安全:Transfer Security
WCF 传输安全模式包含5种方式:None,Transport,Message,Mixed,Both。
WCF安全模式与绑定协议: Net相关的绑定协议默认支持transport安全模式,而WS相关绑定默认支持消息安全模式。 BasicHttpBinding 绑定可支持基本安全配置文件,而 WSHttpBinding 绑定则支持最新的安全标准,例如 WS-Security 1.1 和 WS-SecureConversation。通过对这些标准的支持,WCF 安全性可与除 Microsoft Windows 之外的操作系统和平台上承载的 Web 服务进行互操作和集成。具体关系可以参考下表:
绑定\安全模式
None
Transport
Message
Mixed
Both
BasicHttpBinding
Yes (Default)
Yes
Yes
Yes
No
NetTcpBinding
Yes
Yes (Default)
Yes
Yes
No
NetPeerTcpBinding
Yes
Yes (Default)
Yes
Yes
No
NetNamedPipeBinding
Yes
Yes (Default)
No
No
No
WSHttpBinding
Yes
Yes
Yes (Default)
Yes
No
WSFederationHttpBinding
Yes
No
Yes (Default)
Yes
No
WSDualHttpBinding
Yes
No
Yes (Default)
No
No
NetMsmqBinding
Yes
Yes (Default)
Yes
No
Yes
Transport安全模式与客户端凭据: Transport安全模式与客户端验证方式包括以下4种:None,Windows,UserName,Certificate也就是证书(非对称加密算法里,包含公钥等信息的一种文件形式)。客户端凭据中文翻译别扭,不好理解。clientCredential。通俗来说:就是用什么样的方式来验证客户端。即客户端提供的证件。具体由服务端决定使用哪种方式。下面是绑定协议和客户端验证方式在transport模式下的对应关系:
绑定\客户端凭据
None
Windows
Username
Certificate
BasicHttpBinding
Yes (Default)
Yes
Yes
Yes
NetTcpBinding
Yes
Yes (Default)
No
Yes
NetPeerTcpBinding
No
No
Yes (Default)
Yes
NetNamedPipeBinding
No
Yes (Default)
No
No
WSHttpBinding
Yes
Yes (Default)
Yes
Yes
WSFederationHttpBinding
N/A
N/A
N/A
N/A
WSDualHttpBinding
N/A
N/A
N/A
N/A
NetMsmqBinding
Yes
Yes (Default)
No
Yes
相对tansport安全模式来说,消息安全模式下我们可以多使用一种客户端验证方式:Issued token令牌。消息安全模式增加支持的安全令牌机制。 IssuedToken:安全令牌的概念在WSE3.0里曾经涉及到。它允许您的服务从安全性令牌服务 (STS) 接受一组签名的声明。因为它可以启用联合标识方案和 InfoCard。当您与某个合作伙伴组织联合时,您将允许该合作伙伴通过任何合适的技术对其自己的用户进行身份验证。在最理想的情况下,这将允许该合作伙伴组织中的用户通过单一登录使用您的服务,即便他们并不与您使用同一个 Active Directory 域,或不受您的信任。该合作伙伴组织中的用户需要使用 STS 进行身份验证,而 STS 可以发出一个签名的安全声明标记语言 (Security Assertion Markup Language, SAML) 令牌。您既可以直接接受该令牌,也可以要求将该令牌呈送给您组织中的 STS,以便让其评估该合作伙伴的声明,并发出第二个您可以使用的 SAML 令牌。理解起来有点复杂。实际也是一个标识,鉴别客户端的一个标识。就是一种更加灵活的身份验证方式。 这里主要是为什么transport模式不支持,而消息模式支持,因为安全令牌,需要后续组织中的一个成员进行后续的身份鉴别,然后进行验证结果的共享。Transportat模式只限制点对点传输安全,因而不适合这种验证方式。绑定\客户端凭据
None
Windows
Username
Certificate
Issued token
BasicHttpBinding
No
No
No
Yes
No
NetTcpBinding
Yes
Yes (Default)
Yes
Yes
Yes
NetPeerTcpBinding
N/A
N/A
N/A
N/A
N/A
NetNamedPipeBinding
N/A
N/A
N/A
N/A
N/A
WSHttpBinding
Yes
Yes (Default)
Yes
Yes
Yes
WSFederationHttpBinding
N/A
N/A
N/A
N/A
N/A
WSDualHttpBinding
Yes
Yes (Default)
Yes
Yes
Yes
NetMsmqBinding
Yes
Yes (Default)
Yes
Yes
Yes
四:Basic身份验证实例:
1.创建WCF服务HelloWorldService:
新建类库HelloWorldService,添加IHelloWorldService.cs, HelloWorldService.cs:
[ServiceContract] public interface IHelloWorldService { [OperationContract] string GetMessage(string name); } public class HelloWorldService : IHelloWorldService { public string GetMessage(string name) { return "Hello world from " + name + "!"; } }创建Web.config文件: <?xml version="1.0"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=169433 --> <configuration> <system.web> <compilation debug="true" targetFramework="4.6.1" /> <httpRuntime targetFramework="4.6.1" /> </system.web> <system.serviceModel> <services> <service name="MyWCFServices.HelloWorldService"> <endpoint address="" binding="basicHttpBinding" contract="MyWCFServices.IHelloWorldService" bindingConfiguration="secureHttpBinding"> <identity> <dns value="localhost"/> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> <bindings> <basicHttpBinding> <binding name="secureHttpBinding"> <security mode="Transport"> <transport clientCredentialType="Basic"/> </security> </binding> </basicHttpBinding> </bindings> <serviceHostingEnvironment > <serviceActivations> <add factory="System.ServiceModel.Activation.ServiceHostFactory" relativeAddress="./HostDevServer/HelloWorldService.svc" service="MyWCFServices.HelloWorldService"/> </serviceActivations> </serviceHostingEnvironment> <behaviors> <serviceBehaviors> <behavior> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>2.使用IIS寄宿服务:
首先build HelloWorldService服务,将bin/debug目录下的所有文件拷贝到bin目录里; 在iis中创建site:HelloWorldServiceSecure,目录指向HelloWorldService服务的目录。
3.开启Windows中的Basic 认证:
4.在IIS中,选中HelloWorldServiceSecure 网站,右边双击Authentication ,
开启Basic Authentication:
点击右边的Edit链接,输入计算机的域名(如果有的话):
5.配置https协议:
由于Basic认证中的用户名和密码是通过base64文本传输的,很容易被解码,所以必须使用Https 协议;
6.修改HelloWorldServiceSecure网站的绑定方式为https,并设置SSL certificate为MyTestCert:
7.此时,在浏览器中输入:
https://longxi/HostDevServer/HelloWorldService.svc(longxi为计算机名称),会提示网站不安全,点击Go on to the webpage.会弹出身份验证输入框,输入windows用户名和密码:
则可以成功访问:
8.创建客户端调用程序:HelloWorldClientSecure : 添加服务引用(要带域名):http://longxi.fareast.corp.microsoft.com/HostDevServer/HelloWorldService.svc
会提示输入用户名和密码:
此时,app.config中的内容如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_IHelloWorldService"> <security mode="Transport"> <transport clientCredentialType="Basic" /> </security> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="https://longxi.fareast.corp.microsoft.com/HostDevServer/HelloWorldService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IHelloWorldService" contract="HelloWorldServiceRef.IHelloWorldService" name="BasicHttpBinding_IHelloWorldService" /> </client> </system.serviceModel> </configuration>调用服务:
static void Main(string[] args) { var client = new HelloWorldClient.HelloWorldServiceRef.HelloWorldServiceClient(); client.ClientCredentials.UserName.UserName = "v-lozhu"; client.ClientCredentials.UserName.Password = "!1@2(9qaz"; Console.WriteLine(client.GetMessage("test")); }五、windows身份认证实例:
使用Basic认证的时候,客户端程序必须获取用户的证书(即用户名和密码),这个证书被写死在程序中或者程序弹出输入框让用户输入,这种方式更适合在外网使用。在内网访问的时候,适合使用Windows认证,无需获取用户的证书,而是使用用户的网络证书令牌(即当前登陆计算即的用户的用户名和密码)。
在IIS中,启用WIndows认证。
在web.config中,将basic改为Windows
客户端程序中更新服务引用,此时,不会弹出输入用户名和密码的提示框,因为windows认证使用的是当前登录计算机的用户名和密码。
客户端程序 调用wcf服务:
static void Main(string[] args) { var client = new HelloWorldClient.HelloWorldServiceRef.HelloWorldServiceClient(); client.ChannelFactory.Credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials; Console.WriteLine(client.GetMessage("test")); }