本文提供了一个引导步骤,讲解了如何在WCF解决方案中采用WCS作为身份验证方式。本文假设你已经清楚地了解了WCF的各项机制。
第一部分:服务部分
1. 服务契约
using System.ServiceModel;
namespace HelloService
{
[ServiceContract]
public interface IHello
{
[OperationContract]
string Say();
}
}
2. 服务实现和宿主程序
需要预先引用System.ServiceModel,System.IdentityModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.IdentityModel.Claims;
using System.IdentityModel.Policy;
namespace HelloService
{
class Hello : IHello
{
#region IHello 成员
public string Say()
{
GetIdentity();
return "Hello,World";
}
private void GetIdentity()
{
AuthorizationContext ctx =
OperationContext.Current.ServiceSecurityContext.AuthorizationContext;
foreach (ClaimSet claimSet in ctx.ClaimSets)
{
foreach (Claim claim in claimSet)
{
Console.WriteLine();
Console.WriteLine("ClaimType: " + claim.ClaimType);
Console.WriteLine("Resource: " + claim.Resource);
Console.WriteLine("Right: " + claim.Right);
}
}
return;
}
#endregion
}
class Program
{
static void Main(string[] args)
{
ServiceHost sh = new ServiceHost(typeof(Hello), new Uri("http://localhost:4123/helloService"));
sh.Open();
Console.WriteLine("Service listening...");
Console.WriteLine("Press
to finish"
);
Console.ReadLine();
sh.Close(); // close the service
}
}
}
代码中的GetIdentity方法是我们自己写的,主要是为了解析Windows Cardspace传递过来的凭据信息。
3. 服务配置
xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service
name="HelloService.Hello"
behaviorConfiguration="helloServiceBehavior">
<endpoint
address="helloEndpoint"
contract="HelloService.IHello"
binding="wsFederationHttpBinding"
bindingConfiguration="helloFederatedBinding">
<identity>
<certificateReference
findValue="WCFTestCert"
storeLocation="CurrentUser"
storeName="My"
x509FindType="FindBySubjectName" />
identity>
endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
service>
services>
<bindings>
<wsHttpBinding>
<binding name="helloBinding">
<security mode="Message">
<message clientCredentialType="IssuedToken" />
security>
binding>
wsHttpBinding> <wsFederationHttpBinding> <binding name="helloFederatedBinding" > <security mode="Message"> <message algorithmSuite="Basic128" issuedTokenType="urn:oasis:names:tc:SAML:1.0:assertion" issuedKeyType="SymmetricKey"> <issuer address="http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self"/> <claimTypeRequirements> <add claimType ="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"/> <add claimType ="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"/>
claimTypeRequirements>
message>
security>
binding>
wsFederationHttpBinding>
bindings> <behaviors> <serviceBehaviors> <behavior name="helloServiceBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceCredentials> <issuedTokenAuthentication allowUntrustedRsaIssuers="true" /> <serviceCertificate findValue="WCFTestCert" storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName" />
serviceCredentials>
behavior>
serviceBehaviors>
behaviors>
system.serviceModel>
configuration>
在这里,我们指定了一个特殊的binding(wsFederationHttpBinding)这是专用于WCS的。并且我们声明了当前应用程序需要两部分信息,一个是emailaddress,一个是givename
需要注意的是,我们需要在服务端设置证书,并且在endpoint对其进行引用,这是什么道理呢? 因为客户端也要对服务进行验证。
第二部分:客户端部分
1. 服务契约
using System.ServiceModel;
namespace HelloService
{
[ServiceContract]
public interface IHello
{
[OperationContract]
string Say();
}
}
2. 客户端代码
using System;
using System.ServiceModel;
namespace Client
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hit any key when service is ready");
Console.ReadKey();
ChannelFactory
cnFactory =
new ChannelFactory
(
"helloClient");
HelloService.IHello chn = cnFactory.CreateChannel();
Console.WriteLine(chn.Say());
// Clean up
cnFactory.Close();
// close the client’s channel
Console.WriteLine(
"Press
to finish"
);
Console.ReadLine();
}
}
}
3. 客户端配置
xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint
name="helloClient"
address="http://localhost:4123/helloService/helloEndpoint"
contract="HelloService.IHello"
binding="wsFederationHttpBinding"
bindingConfiguration="helloFederatedBinding"
behaviorConfiguration="helloClientBehavior">
<identity>
<certificateReference
findValue="WCFTestCert"
storeLocation="CurrentUser"
storeName="My"
x509FindType="FindBySubjectName" />
identity>
endpoint>
client>
<bindings>
<wsHttpBinding>
<binding name="helloBinding">
<security mode="Message">
<message clientCredentialType="IssuedToken" />
security>
binding>
wsHttpBinding> <wsFederationHttpBinding> <binding name="helloFederatedBinding" > <security mode="Message"> <message algorithmSuite="Basic128" issuedTokenType="urn:oasis:names:tc:SAML:1.0:assertion" issuedKeyType="SymmetricKey"> <issuer address="http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self"/> <claimTypeRequirements> <add claimType ="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"/> <add claimType ="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"/>
claimTypeRequirements>
message>
security>
binding>
wsFederationHttpBinding>
bindings> <behaviors> <endpointBehaviors> <behavior name="helloClientBehavior"> <clientCredentials> <serviceCertificate> <authentication trustedStoreLocation="CurrentUser" revocationMode="NoCheck"/> <defaultCertificate findValue="WCFTestCert" storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName" />
serviceCertificate>
clientCredentials>
behavior>
endpointBehaviors>
behaviors>
system.serviceModel>
configuration>
也就是说,在客户端也是需要包含证书的。一般是把不包含私钥的证书在客户端安装。
4. 运行效果
服务端
客户端
[备注]因为是Session的隔离,所以选择卡片的界面是没有办法截图的。
4. 为客户端实现异常处理
添加System.IdentityModel.Selectors引用
using System;
using System.ServiceModel;
using System.IdentityModel.Selectors;
namespace Client
{
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Hit any key when service is ready");
Console.ReadKey();
ChannelFactory
cnFactory =
new
ChannelFactory
(
"helloClient");
HelloService.IHello chn = cnFactory.CreateChannel();
Console.WriteLine(chn.Say());
cnFactory.Close();
}
catch (UserCancellationException)
{
Console.WriteLine(
"User cancelled");
}
catch (UntrustedRecipientException)
{
Console.WriteLine(
"User does not trust the recipient");
}
catch (ServiceNotStartedException)
{
Console.WriteLine(
"Cardspace service not started");
}
catch (CardSpaceException cse)
{
Console.WriteLine(
"Generic Cardspace exception:" + cse.Message);
}
catch (Exception e)
{
Console.WriteLine(
"Other exceptions :" + e.Message);
}
finally
{
Console.WriteLine(
"Press
to finish"
);
Console.ReadLine();
}
}
}
}