此範例示範如何將自定義令牌實作新增至 Windows Communication Foundation (WCF) 應用程式。 此範例會使用 CreditCardToken
,安全地將用戶端信用卡的相關信息傳遞至服務。 傳遞於 WS-Security 訊息標頭中的令牌,會使用對稱安全性綁定元素連同訊息正文及其他訊息標頭一起簽署和加密。 在內建令牌不足的情況下,這非常有用。 此範例示範如何將自定義安全性令牌提供給服務,而不是使用其中一個內建令牌。 服務會實作定義要求-回復通訊模式的合約。
備註
此範例的安裝程式和建置指示位於本主題結尾。
為了摘要說明,此範例示範下列各項:
用戶端如何將自定義安全性令牌傳遞至服務。
服務如何取用及驗證自定義安全性令牌。
WCF 服務程式代碼如何取得所接收安全性令牌的相關信息,包括自定義安全性令牌。
如何使用伺服器的 X.509 憑證來保護用於訊息加密和簽章的對稱密鑰。
使用自訂安全性令牌的客戶端驗證
服務會公開以程序設計方式使用 BindingHelper
和 EchoServiceHost
類別建立的單一端點。 端點是由位址、系結和合約所組成。 系結是使用 SymmetricSecurityBindingElement
和 HttpTransportBindingElement
來設定自定義系結。 此範例會將 SymmetricSecurityBindingElement
設定為使用服務的 X.509 憑證,在傳輸期間保護對稱密鑰,並將 WS-Security 訊息標頭中的自定義 CreditCardToken
傳遞為已簽署和加密的安全性令牌。 行為會指定要用於客戶端驗證的服務認證,以及服務 X.509 憑證的相關信息。
public static class BindingHelper
{
public static Binding CreateCreditCardBinding()
{
var httpTransport = new HttpTransportBindingElement();
// The message security binding element will be configured to require a credit card.
// The token that is encrypted with the service's certificate.
var messageSecurity = new SymmetricSecurityBindingElement();
messageSecurity.EndpointSupportingTokenParameters.SignedEncrypted.Add(new CreditCardTokenParameters());
X509SecurityTokenParameters x509ProtectionParameters = new X509SecurityTokenParameters();
x509ProtectionParameters.InclusionMode = SecurityTokenInclusionMode.Never;
messageSecurity.ProtectionTokenParameters = x509ProtectionParameters;
return new CustomBinding(messageSecurity, httpTransport);
}
}
為了在訊息中取用信用卡令牌,此範例會使用自定義服務認證來提供這項功能。 服務認證類別位於 類別中 CreditCardServiceCredentials
,並且會新增至 方法中 EchoServiceHost.InitializeRuntime
服務主機的行為集合。
class EchoServiceHost : ServiceHost
{
string creditCardFile;
public EchoServiceHost(parameters Uri[] addresses)
: base(typeof(EchoService), addresses)
{
creditCardFile = ConfigurationManager.AppSettings["creditCardFile"];
if (string.IsNullOrEmpty(creditCardFile))
{
throw new ConfigurationErrorsException("creditCardFile not specified in service config");
}
creditCardFile = String.Format("{0}\\{1}", System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath, creditCardFile);
}
override protected void InitializeRuntime()
{
// Create a credit card service credentials and add it to the behaviors.
CreditCardServiceCredentials serviceCredentials = new CreditCardServiceCredentials(this.creditCardFile);
serviceCredentials.ServiceCertificate.SetCertificate("CN=localhost", StoreLocation.LocalMachine, StoreName.My);
this.Description.Behaviors.Remove((typeof(ServiceCredentials)));
this.Description.Behaviors.Add(serviceCredentials);
// Register a credit card binding for the endpoint.
Binding creditCardBinding = BindingHelper.CreateCreditCardBinding();
this.AddServiceEndpoint(typeof(IEchoService), creditCardBinding, string.Empty);
base.InitializeRuntime();
}
}
用戶端端點會以與服務端點類似的方式進行設定。 用戶端會使用相同的 BindingHelper
類別來建立系結。 其餘的安裝程式位於類別中 Client
。 客戶端還會設置CreditCardToken
中要包含的信息,以及設定程式代碼中關於服務 X.509 憑證的信息,這是藉由將具有適當數據的CreditCardClientCredentials
實例新增到客戶端端點行為集合中來完成的。 此範例會使用包含CN=localhost
主體名稱的 X.509 憑證作為服務憑證。
Binding creditCardBinding = BindingHelper.CreateCreditCardBinding();
var serviceAddress = new EndpointAddress("http://localhost/servicemodelsamples/service.svc");
// Create a client with given client endpoint configuration.
channelFactory = new ChannelFactory<IEchoService>(creditCardBinding, serviceAddress);
// Configure the credit card credentials on the channel factory.
var credentials =
new CreditCardClientCredentials(
new CreditCardInfo(creditCardNumber, issuer, expirationTime));
// Configure the service certificate on the credentials.
credentials.ServiceCertificate.SetDefaultCertificate(
"CN=localhost", StoreLocation.LocalMachine, StoreName.My);
// Replace ClientCredentials with CreditCardClientCredentials.
channelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
channelFactory.Endpoint.Behaviors.Add(credentials);
client = channelFactory.CreateChannel();
Console.WriteLine($"Echo service returned: {client.Echo()}");
((IChannel)client).Close();
channelFactory.Close();
自訂安全性令牌實作
若要在 WCF 中啟用自訂安全性令牌,請建立自定義安全性令牌的物件表示法。 此範例在 CreditCardToken
類別中具有這個表示法。 物件表示負責保存所有相關的安全性令牌資訊,並提供安全性令牌中包含的安全性密鑰清單。 在此情況下,信用卡安全性令牌不包含任何安全性密鑰。
下一節說明必須執行哪些動作,才能透過網路傳輸自定義令牌,並由WCF端點取用。
class CreditCardToken : SecurityToken
{
CreditCardInfo cardInfo;
DateTime effectiveTime = DateTime.UtcNow;
string id;
ReadOnlyCollection<SecurityKey> securityKeys;
public CreditCardToken(CreditCardInfo cardInfo) : this(cardInfo, Guid.NewGuid().ToString()) { }
public CreditCardToken(CreditCardInfo cardInfo, string id)
{
if (cardInfo == null)
throw new ArgumentNullException(nameof(cardInfo));
if (id == null)
throw new ArgumentNullException(nameof(id));
this.cardInfo = cardInfo;
this.id = id;
// The credit card token is not capable of any cryptography.
this.securityKeys = new ReadOnlyCollection<SecurityKey>(new List<SecurityKey>());
}
public CreditCardInfo CardInfo { get { return this.cardInfo; } }
public override ReadOnlyCollection<SecurityKey> SecurityKeys { get { return this.securityKeys; } }
public override DateTime ValidFrom { get { return this.effectiveTime; } }
public override DateTime ValidTo { get { return this.cardInfo.ExpirationDate; } }
public override string Id { get { return this.id; } }
}
在訊息中傳送和接收自定義信用卡令牌
WCF 中的安全性令牌串行化程式負責從訊息中的 XML 建立安全性令牌的物件表示,以及建立安全性令牌的 XML 形式。 它們也會負責其他功能,例如讀取和寫入指向安全性令牌的密鑰標識碼,但此範例只會使用安全性令牌相關功能。 若要啟用自定義令牌,您必須實作自己的安全性令牌串行化程式。 此範例使用 CreditCardSecurityTokenSerializer
類別來達成此目的。
在服務上,自定義串行化程式會讀取自定義令牌的 XML 格式,並從中建立自定義令牌物件表示法。
在用戶端上,類別 CreditCardSecurityTokenSerializer
會將安全性令牌物件表示中包含的資訊寫入 XML 寫入器中。
public class CreditCardSecurityTokenSerializer : WSSecurityTokenSerializer
{
public CreditCardSecurityTokenSerializer(SecurityTokenVersion version) : base() { }
protected override bool CanReadTokenCore(XmlReader reader)
{
XmlDictionaryReader localReader = XmlDictionaryReader.CreateDictionaryReader(reader);
if (reader == null)
throw new ArgumentNullException(nameof(reader));
if (reader.IsStartElement(Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace))
return true;
return base.CanReadTokenCore(reader);
}
protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));
if (reader.IsStartElement(Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace))
{
string id = reader.GetAttribute(Constants.Id, Constants.WsUtilityNamespace);
reader.ReadStartElement();
// Read the credit card number.
string creditCardNumber = reader.ReadElementString(Constants.CreditCardNumberElementName, Constants.CreditCardTokenNamespace);
// Read the expiration date.
string expirationTimeString = reader.ReadElementString(Constants.CreditCardExpirationElementName, Constants.CreditCardTokenNamespace);
DateTime expirationTime = XmlConvert.ToDateTime(expirationTimeString, XmlDateTimeSerializationMode.Utc);
// Read the issuer of the credit card.
string creditCardIssuer = reader.ReadElementString(Constants.CreditCardIssuerElementName, Constants.CreditCardTokenNamespace);
reader.ReadEndElement();
var cardInfo = new CreditCardInfo(creditCardNumber, creditCardIssuer, expirationTime);
return new CreditCardToken(cardInfo, id);
}
else
{
return WSSecurityTokenSerializer.DefaultInstance.ReadToken(reader, tokenResolver);
}
}
protected override bool CanWriteTokenCore(SecurityToken token)
{
if (token is CreditCardToken)
return true;
return base.CanWriteTokenCore(token);
}
protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
{
if (writer == null)
throw new ArgumentNullException(nameof(writer));
if (token == null)
throw new ArgumentNullException(nameof(token));
CreditCardToken c = token as CreditCardToken;
if (c != null)
{
writer.WriteStartElement(Constants.CreditCardTokenPrefix, Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace);
writer.WriteAttributeString(Constants.WsUtilityPrefix, Constants.Id, Constants.WsUtilityNamespace, token.Id);
writer.WriteElementString(Constants.CreditCardNumberElementName, Constants.CreditCardTokenNamespace, c.CardInfo.CardNumber);
writer.WriteElementString(Constants.CreditCardExpirationElementName, Constants.CreditCardTokenNamespace, XmlConvert.ToString(c.CardInfo.ExpirationDate, XmlDateTimeSerializationMode.Utc));
writer.WriteElementString(Constants.CreditCardIssuerElementName, Constants.CreditCardTokenNamespace, c.CardInfo.CardIssuer);
writer.WriteEndElement();
writer.Flush();
}
else
{
base.WriteTokenCore(writer, token);
}
}
}
如何建立令牌提供者和令牌驗證器類別
用戶端和服務認證負責提供安全性令牌管理員實例。 安全性令牌管理員實例可用來取得令牌提供者、令牌驗證器和令牌串行化程式。
令牌提供者會根據客戶端或服務認證中包含的資訊,建立令牌的物件表示法。 然後,令牌物件表示法會使用令牌串行化程式寫入訊息(如上一節所述)。
令牌驗證器會驗證包含於訊息中的令牌。 傳入的令牌物件表示是由令牌串行化程式所建立。 然後,這個物件表示法會傳遞至令牌驗證器以進行驗證。 成功驗證令牌之後,令牌驗證器會傳回 物件的集合 IAuthorizationPolicy
,這些物件表示令牌中包含的資訊。 此資訊將在稍後的訊息處理期間使用,以執行授權決策並提供應用程式的宣稱。 在此範例中,信用卡令牌驗證器會針對此用途使用 CreditCardTokenAuthorizationPolicy
。
令牌串行化程式負責從網路來回取得令牌的物件表示法。 這在上一節中討論。
在此範例中,我們只在用戶端上使用令牌提供者,而只會在服務上使用令牌驗證器,因為我們只想在用戶端對服務方向中傳輸信用卡令牌。
用戶端上的功能位於 CreditCardClientCredentials
、 CreditCardClientCredentialsSecurityTokenManager
類別 CreditCardTokenProvider
中。
在服務上,功能位於CreditCardServiceCredentials
、 CreditCardServiceCredentialsSecurityTokenManager
CreditCardTokenAuthenticator
和 CreditCardTokenAuthorizationPolicy
類別中。
public class CreditCardClientCredentials : ClientCredentials
{
CreditCardInfo creditCardInfo;
public CreditCardClientCredentials(CreditCardInfo creditCardInfo)
: base()
{
if (creditCardInfo == null)
throw new ArgumentNullException(nameof(creditCardInfo));
this.creditCardInfo = creditCardInfo;
}
public CreditCardInfo CreditCardInfo
{
get { return this.creditCardInfo; }
}
protected override ClientCredentials CloneCore()
{
return new CreditCardClientCredentials(this.creditCardInfo);
}
public override SecurityTokenManager CreateSecurityTokenManager()
{
return new CreditCardClientCredentialsSecurityTokenManager(this);
}
}
public class CreditCardClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager
{
CreditCardClientCredentials creditCardClientCredentials;
public CreditCardClientCredentialsSecurityTokenManager(CreditCardClientCredentials creditCardClientCredentials)
: base (creditCardClientCredentials)
{
this.creditCardClientCredentials = creditCardClientCredentials;
}
public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
{
// Handle this token for Custom.
if (tokenRequirement.TokenType == Constants.CreditCardTokenType)
return new CreditCardTokenProvider(this.creditCardClientCredentials.CreditCardInfo);
// Return server cert.
else if (tokenRequirement is InitiatorServiceModelSecurityTokenRequirement)
{
if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
{
return new X509SecurityTokenProvider(creditCardClientCredentials.ServiceCertificate.DefaultCertificate);
}
}
return base.CreateSecurityTokenProvider(tokenRequirement);
}
public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
{
return new CreditCardSecurityTokenSerializer(version);
}
}
class CreditCardTokenProvider : SecurityTokenProvider
{
CreditCardInfo creditCardInfo;
public CreditCardTokenProvider(CreditCardInfo creditCardInfo) : base()
{
if (creditCardInfo == null)
throw new ArgumentNullException(nameof(creditCardInfo));
this.creditCardInfo = creditCardInfo;
}
protected override SecurityToken GetTokenCore(TimeSpan timeout)
{
SecurityToken result = new CreditCardToken(this.creditCardInfo);
return result;
}
}
public class CreditCardServiceCredentials : ServiceCredentials
{
string creditCardFile;
public CreditCardServiceCredentials(string creditCardFile)
: base()
{
if (creditCardFile == null)
throw new ArgumentNullException(nameof(creditCardFile));
this.creditCardFile = creditCardFile;
}
public string CreditCardDataFile
{
get { return this.creditCardFile; }
}
protected override ServiceCredentials CloneCore()
{
return new CreditCardServiceCredentials(this.creditCardFile);
}
public override SecurityTokenManager CreateSecurityTokenManager()
{
return new CreditCardServiceCredentialsSecurityTokenManager(this);
}
}
public class CreditCardServiceCredentialsSecurityTokenManager : ServiceCredentialsSecurityTokenManager
{
CreditCardServiceCredentials creditCardServiceCredentials;
public CreditCardServiceCredentialsSecurityTokenManager(CreditCardServiceCredentials creditCardServiceCredentials)
: base(creditCardServiceCredentials)
{
this.creditCardServiceCredentials = creditCardServiceCredentials;
}
public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
{
if (tokenRequirement.TokenType == Constants.CreditCardTokenType)
{
outOfBandTokenResolver = null;
return new CreditCardTokenAuthenticator(creditCardServiceCredentials.CreditCardDataFile);
}
return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
}
public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
{
return new CreditCardSecurityTokenSerializer(version);
}
}
class CreditCardTokenAuthenticator : SecurityTokenAuthenticator
{
string creditCardsFile;
public CreditCardTokenAuthenticator(string creditCardsFile)
{
this.creditCardsFile = creditCardsFile;
}
protected override bool CanValidateTokenCore(SecurityToken token)
{
return (token is CreditCardToken);
}
protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)
{
CreditCardToken creditCardToken = token as CreditCardToken;
if (creditCardToken.CardInfo.ExpirationDate < DateTime.UtcNow)
throw new SecurityTokenValidationException("The credit card has expired.");
if (!IsCardNumberAndExpirationValid(creditCardToken.CardInfo))
throw new SecurityTokenValidationException("Unknown or invalid credit card.");
// the credit card token has only 1 claim - the card number. The issuer for the claim is the
// credit card issuer
var cardIssuerClaimSet = new DefaultClaimSet(new Claim(ClaimTypes.Name, creditCardToken.CardInfo.CardIssuer, Rights.PossessProperty));
var cardClaimSet = new DefaultClaimSet(cardIssuerClaimSet, new Claim(Constants.CreditCardNumberClaim, creditCardToken.CardInfo.CardNumber, Rights.PossessProperty));
var policies = new List<IAuthorizationPolicy>(1);
policies.Add(new CreditCardTokenAuthorizationPolicy(cardClaimSet));
return policies.AsReadOnly();
}
/// <summary>
/// Helper method to check if a given credit card entry is present in the User DB
/// </summary>
private bool IsCardNumberAndExpirationValid(CreditCardInfo cardInfo)
{
try
{
using (var myStreamReader = new StreamReader(this.creditCardsFile))
{
string line = "";
while ((line = myStreamReader.ReadLine()) != null)
{
string[] splitEntry = line.Split('#');
if (splitEntry[0] == cardInfo.CardNumber)
{
string expirationDateString = splitEntry[1].Trim();
DateTime expirationDateOnFile = DateTime.Parse(expirationDateString, System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.AdjustToUniversal);
if (cardInfo.ExpirationDate == expirationDateOnFile)
{
string issuer = splitEntry[2];
return issuer.Equals(cardInfo.CardIssuer, StringComparison.InvariantCultureIgnoreCase);
}
else
{
return false;
}
}
}
return false;
}
}
catch (Exception e)
{
throw new Exception("BookStoreService: Error while retrieving credit card information from User DB " + e.ToString());
}
}
}
public class CreditCardTokenAuthorizationPolicy : IAuthorizationPolicy
{
string id;
ClaimSet issuer;
IEnumerable<ClaimSet> issuedClaimSets;
public CreditCardTokenAuthorizationPolicy(ClaimSet issuedClaims)
{
if (issuedClaims == null)
throw new ArgumentNullException(nameof(issuedClaims));
this.issuer = issuedClaims.Issuer;
this.issuedClaimSets = new ClaimSet[] { issuedClaims };
this.id = Guid.NewGuid().ToString();
}
public ClaimSet Issuer { get { return this.issuer; } }
public string Id { get { return this.id; } }
public bool Evaluate(EvaluationContext context, ref object state)
{
foreach (ClaimSet issuance in this.issuedClaimSets)
{
context.AddClaimSet(this, issuance);
}
return true;
}
}
顯示來電者的資訊
若要顯示呼叫端的資訊,請使用 ServiceSecurityContext.Current.AuthorizationContext.ClaimSets
,如下列範例程式代碼所示。
ServiceSecurityContext.Current.AuthorizationContext.ClaimSets
包含與目前呼叫端相關聯的授權宣告。 宣告是由CreditCardToken
類別在其AuthorizationPolicies
集合中提供。
bool TryGetStringClaimValue(ClaimSet claimSet, string claimType, out string claimValue)
{
claimValue = null;
IEnumerable<Claim> matchingClaims = claimSet.FindClaims(claimType, Rights.PossessProperty);
if (matchingClaims == null)
return false;
IEnumerator<Claim> enumerator = matchingClaims.GetEnumerator();
enumerator.MoveNext();
claimValue = (enumerator.Current.Resource == null) ? null :
enumerator.Current.Resource.ToString();
return true;
}
string GetCallerCreditCardNumber()
{
foreach (ClaimSet claimSet in
ServiceSecurityContext.Current.AuthorizationContext.ClaimSets)
{
string creditCardNumber = null;
if (TryGetStringClaimValue(claimSet,
Constants.CreditCardNumberClaim, out creditCardNumber))
{
string issuer;
if (!TryGetStringClaimValue(claimSet.Issuer,
ClaimTypes.Name, out issuer))
{
issuer = "Unknown";
}
return $"Credit card '{creditCardNumber}' issued by '{issuer}'";
}
}
return "Credit card is not known";
}
當您執行範例時,作業要求和回應會顯示在用戶端控制台視窗中。 在客戶端視窗中按 ENTER 鍵以關閉用戶端。
設定批次處理檔
此範例隨附的 Setup.bat 批處理檔可讓您設定具有相關憑證的伺服器,以執行需要伺服器證書式安全性的 IIS 裝載應用程式。 必須修改此批處理檔,才能跨電腦使用,或在非托管的情況下操作。
下列提供批處理檔不同區段的簡短概觀,以便修改它們以在適當的組態中執行。
建立伺服器憑證:
批處理檔中的下列幾行
Setup.bat
會建立要使用的伺服器證書。 變數%SERVER_NAME%
會指定伺服器名稱。 變更此變數以指定您自己的伺服器名稱。 此批處理檔中的預設值為localhost。 如果您變更%SERVER_NAME%
變數,您必須完成Client.cs和Service.cs檔案,並以您在 Setup.bat 腳本中使用的伺服器名稱取代 localhost 的所有實例。憑證會儲存在
LocalMachine
存放區位置下的「個人」(我的)存放區中。 憑證會儲存在 IIS 裝載服務的 LocalMachine 存放區中。 針對自我裝載服務,您應該修改批處理檔,將用戶端憑證儲存在 CurrentUser 存放區位置,方法是將字串 LocalMachine 取代為 CurrentUser。echo ************ echo Server cert setup starting echo %SERVER_NAME% echo ************ echo making server cert echo ************ makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
將伺服器憑證安裝到用戶端的受信任憑證儲存中:
Setup.bat 批處理檔中的下列幾行會將伺服器證書複製到用戶端信任的人員存放區。 這是必要步驟,因為客戶端系統不會隱含信任 Makecert.exe 所產生的憑證。 如果您已經有根植於用戶端受信任的根證書的憑證,例如,Microsoft 發行的憑證,則不需要執行使用伺服器證書填入用戶端證書存儲的此步驟。
echo ************ echo copying server cert to client's TrustedPeople store echo ************ certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
若要從 IIS 裝載的服務啟用憑證私鑰的存取權,該使用者帳戶必須被授與適當的權限,才能在 IIS 上執行的過程中使用私鑰。 這是由 Setup.bat 程式碼中的最後步驟來完成。
echo ************ echo setting privileges on server certificates echo ************ for /F "delims=" %%i in ('"%ProgramFiles%\ServiceModelSampleTools\FindPrivateKey.exe" My LocalMachine -n CN^=%SERVER_NAME% -a') do set PRIVATE_KEY_FILE=%%i set WP_ACCOUNT=NT AUTHORITY\NETWORK SERVICE (ver | findstr /C:"5.1") && set WP_ACCOUNT=%COMPUTERNAME%\ASPNET echo Y|cacls.exe "%PRIVATE_KEY_FILE%" /E /G "%WP_ACCOUNT%":R iisreset
備註
Setup.bat 批處理文件的設計目的是要從 Visual Studio 命令提示字元執行。 Visual Studio 命令提示字元內設定的PATH環境變數會指向包含 Setup.bat 腳本所需可執行檔案的目錄。
若要設定和建置範例
請確定您已針對 Windows Communication Foundation 範例 執行One-Time 安裝程式。
若要建置解決方案,請遵循 建置 Windows Communication Foundation 範例中的指示。
在同一部電腦上執行這個範例
- 使用系統管理員許可權開啟 Visual Studio 命令提示字元視窗,然後從範例安裝資料夾執行 Setup.bat。 這會安裝執行範例所需的所有憑證。請確定路徑包含 Makecert.exe 所在的資料夾。
備註
範例完成後,請務必執行 Cleanup.bat 以移除憑證。 其他安全性範例使用相同的憑證。
從 client\bin 目錄啟動 Client.exe。 用戶端活動會顯示在用戶端主控台應用程式上。
如果客戶端和服務無法通訊,請參閱 WCF 範例 的疑難解答秘訣。
若要在多台電腦上執行範例
在服務電腦上為服務的二進位檔案建立目錄。
將服務程式檔案複製到服務電腦上的服務目錄。 別忘了複製 CreditCardFile.txt;否則,信用卡驗證器無法驗證從用戶端傳送的信用卡資訊。 同時將 Setup.bat 和 Cleanup.bat 檔案複製到服務計算機。
您必須擁有包含電腦完整域名的主體名稱的伺服器證書。 如果您將
%SERVER_NAME%
變數更改為承載服務的計算機的完整名稱,就可以使用 Setup.bat 來建立一個。 請注意,Setup.bat 檔案必須在以系統管理員許可權開啟的Visual Studio開發人員命令提示字元中執行。將伺服器證書複製到用戶端上的 CurrentUser-TrustedPeople 存放區。 只有當伺服器證書不是由受信任的簽發者簽發時,您才能執行此動作。
在EchoServiceHost.cs檔案中,變更憑證主體名稱的值,以指定完整計算機名稱,而不是localhost。
將用戶端程式檔案從語言特定資料夾下的 \client\bin\ 資料夾,複製到用戶端電腦。
在Client.cs檔案中,變更端點的位址值,以符合您服務的新位址。
在Client.cs檔案中,變更服務 X.509 憑證的主體名稱,以符合遠端主機的完整計算機名稱,而不是localhost。
在用戶端電腦上,從命令提示字元窗口啟動 Client.exe。
如果客戶端和服務無法通訊,請參閱 WCF 範例 的疑難解答秘訣。
在範例之後清除
- 在您完成執行範例之後,請在samples資料夾中執行 Cleanup.bat。