WCF 安全涉及服務契約、操作契約、服務行為、繫結、憑證、憑證驗證模式、授權策略等數十個正交配置維度。組合爆炸導致開發者易誤配、難維護。Juval Lowy 提出的聲明式安全框架,將五大典型場景封裝為單一屬性 [SecurityBehavior],實現「選擇場景,自動配置」的開發體驗。
| 場景 | 描述 | 支援繫結 | 傳輸/訊息安全 | 客戶端認證 | 服務認證 | 授權 |
|---|---|---|---|---|---|---|
| Intranet | 內網、雙方同域、Windows 整合 | NetTcpBinding, NetNamedPipeBinding, NetMsmqBinding | 傳輸安全 | Windows | Windows 憑證 | Windows 角色/組 |
| Internet | 跨防火牆、異構客戶端、用戶名密碼 | WSHttpBinding, WSDualHttpBinding | 訊息安全 | UserName (Windows/ASP.NET) | X.509 憑證 | Windows/ASP.NET 角色 |
| BusinessToBusiness | 企業對企業、憑證互信、無共用帳號 | BasicHttpBinding, WSHttpBinding, WSDualHttpBinding | 訊息安全 | X.509 憑證 | X.509 憑證 | ASP.NET 角色 (可選) |
| Anonymous | 匿名存取、需訊息保護、無客戶端認證 | NetTcpBinding, WSHttpBinding, WSDualHttpBinding, NetMsmqBinding | 訊息安全 | 無 | X.509 憑證 | 無 |
| None | 完全無安全、公開端點 | 所有繫結 | 無 | 無 | 無 | 無 |
public enum ServiceSecurity { None, Anonymous, BusinessToBusiness, Internet, Intranet }
[AttributeUsage(AttributeTargets.Class)]
public class SecurityBehaviorAttribute : Attribute, IServiceBehavior
{
public SecurityBehaviorAttribute(ServiceSecurity mode);
public SecurityBehaviorAttribute(ServiceSecurity mode, string serviceCertName);
public SecurityBehaviorAttribute(ServiceSecurity mode,
StoreLocation storeLocation, StoreName storeName,
X509FindType findType, string subjectName);
public bool ImpersonateAll { get; set; } // 自動模擬所有呼叫者
public string ApplicationName { get; set; } // ASP.NET 提供者應用程式名
public bool UseAspNetProviders { get; set; } // 使用 ASP.NET Membership/Role
}
[SecurityBehavior(ServiceSecurity.Intranet)]
[PrincipalPermission(SecurityAction.Demand, Role = @"DOMAIN\Users")]
public class InternalService : IInternalService { ... }
[SecurityBehavior(ServiceSecurity.Internet, "MyServiceCert")]
[PrincipalPermission(SecurityAction.Demand, Role = "Customers")]
public class PublicService : IPublicService { ... }
// 指定憑證存儲位置
[SecurityBehavior(ServiceSecurity.Internet,
StoreLocation.LocalMachine, StoreName.My,
X509FindType.FindBySubjectName, "MyServiceCert")]
public class PublicService2 : IPublicService { ... }
// 使用 ASP.NET 提供者
[SecurityBehavior(ServiceSecurity.Internet, "MyServiceCert",
UseAspNetProviders = true, ApplicationName = "MyApp")]
[PrincipalPermission(SecurityAction.Demand, Role = "Managers")]
public class AspNetService : IAspNetService { ... }
[SecurityBehavior(ServiceSecurity.BusinessToBusiness, "MyServiceCert")]
public class B2BService : IB2BService { ... }
// 啟用 ASP.NET 角色授權
[SecurityBehavior(ServiceSecurity.BusinessToBusiness, "MyServiceCert",
UseAspNetProviders = true, ApplicationName = "B2BPortal")]
public class B2BServiceAuth : IB2BService { ... }
[SecurityBehavior(ServiceSecurity.Anonymous, "MyServiceCert")]
public class AnonymousService : IAnonymousService { ... }
[SecurityBehavior(ServiceSecurity.None)]
public class OpenService : IOpenService { ... }
public class SecurityBehavior : IServiceBehavior
{
// 1. 驗證階段:配置服務憑證、ASP.NET 提供者、模擬設定
public void Validate(ServiceDescription desc, ServiceHostBase host)
{
if (mode == ServiceSecurity.Internet && certName != null)
{
host.Credentials.ServiceCertificate.SetCertificate(...);
}
// 啟用角色管理器、設定 ApplicationName...
}
// 2. 繫結參數階段:強制繫結安全模式、認證類型
public void AddBindingParameters(ServiceDescription desc, ServiceHostBase host,
Collection endpoints, BindingParameterCollection parameters)
{
foreach (var ep in endpoints)
{
switch (mode)
{
case ServiceSecurity.Internet:
ConfigureInternet(ep.Binding, UseAspNetProviders);
break;
case ServiceSecurity.Intranet:
ConfigureIntranet(ep.Binding);
break;
// ...
}
}
}
// 3. 派發行為階段:授權管理器、模擬行為...
public void ApplyDispatchBehavior(ServiceDescription desc, ServiceHostBase host) { ... }
}
internal static void ConfigureInternet(Collection endpoints, bool useAspNet)
{
foreach (var ep in endpoints)
{
if (ep.Binding is WSHttpBinding wsBinding)
{
wsBinding.Security.Mode = SecurityMode.Message;
wsBinding.Security.Message.ClientCredentialType =
useAspNet ? MessageCredentialType.UserName : MessageCredentialType.Windows;
wsBinding.Security.Message.EstablishSecurityContext = true;
continue;
}
throw new InvalidOperationException($"{ep.Binding.GetType()} 不支援 Internet 場景");
}
}
public static class SecurityHelper
{
// Intranet: Windows 認證
public static void SecureProxy(ClientBase proxy, string domain, string user, string pwd);
// Internet: 用戶名密碼
public static void SecureProxy(ClientBase proxy, string user, string pwd);
// B2B: 客戶端憑證
public static void SecureProxy(ClientBase proxy, string clientCertName);
// Anonymous
public static void AnonymousProxy(ClientBase proxy);
// None
public static void UnsecuredProxy(ClientBase proxy);
}
// Internet 場景
var proxy = new MyServiceClient();
SecurityHelper.SecureProxy(proxy, "username", "password");
proxy.DoWork();
// B2B 場景
var proxy2 = new PartnerServiceClient();
SecurityHelper.SecureProxy(proxy2, "ClientCertName");
proxy2.SubmitOrder(order);
public abstract class SecureClientBase : ClientBase where T : class
{
protected SecureClientBase(ServiceSecurity mode);
protected SecureClientBase(string user, string pwd);
protected SecureClientBase(string domain, string user, string pwd);
protected SecureClientBase(string clientCertName);
// ... 對應各場景的建構函數
}
// 衍生 Proxy
public class MyServiceClient : SecureClientBase, IMyService
{
public MyServiceClient(string user, string pwd) : base(user, pwd) { }
public void DoWork() => Channel.DoWork();
}
// 使用
var proxy = new MyServiceClient("user", "pwd");
proxy.DoWork();
聲明式安全框架展示了 WCF 擴展點(IServiceBehavior)的強大威力。透過 [SecurityBehavior] 屬性,開發者只需識別業務屬於哪一種安全場景,框架自動套用經實戰驗證的配置組合。對於 80% 的企業應用,這五大場景已足夠覆蓋。若需超越框架範圍,可繼承 SecurityBehavior 類別,在保留聲明式風格的同時注入自訂邏輯。