服務導向架構中,每次服務呼叫都跨越安全邊界。WCF 提供兩大授權模型:角色基礎授權(簡單、成熟)與聲明基礎授權(強大、靈活、面向未來)。選擇取決於系統複雜度與擴展需求。
IIdentity:身分資訊(姓名、驗證方式、是否已驗證)IPrincipal:角色資訊,核心方法 bool IsInRole(string role)Thread.CurrentPrincipal:執行緒靜態儲存,服務作業透過此存取授權上下文| 策略 | 適用場景 | 配置關鍵 |
|---|---|---|
| Windows 角色 | 內網、AD 環境 | 預設行為,WindowsPrincipal |
| ASP.NET 角色提供者 | 自訂資料庫、表單驗證 | principalPermissionMode="UseAspNetRoles" |
| 自訂 IPrincipal | 複雜業務邏輯、快取、多租戶 | principalPermissionMode="Custom" + 授權策略 |
public class CustomPrincipal : IPrincipal
{
private readonly IIdentity _identity;
private string[] _roles;
private readonly Cache _cache = HttpRuntime.Cache;
public CustomPrincipal(IIdentity identity) => _identity = identity;
public IIdentity Identity => _identity;
public bool IsInRole(string role)
{
EnsureRoles();
return _roles.Contains(role);
}
public string[] Roles
{
get { EnsureRoles(); return _roles; }
}
protected virtual void EnsureRoles()
{
if (_roles != null) return;
// 從資料庫/快取載入角色邏輯
_roles = _cache["Roles_" + _identity.Name] as string[]
?? LoadRolesFromDatabase(_identity.Name);
_cache["Roles_" + _identity.Name] = _roles;
}
}
// 授權策略注入 Principal
public class AuthorizationPolicy : IAuthorizationPolicy
{
public bool Evaluate(EvaluationContext ctx, ref object state)
{
var clientId = GetClientIdentity(ctx);
ctx.Properties["Principal"] = new CustomPrincipal(clientId);
return true;
}
// ... Id, Issuer 實作
}
繼承 ServiceAuthorizationManager 並覆寫 CheckAccessCore,在單一位置攔截所有請求:
public class AuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext ctx)
{
string action = ctx.IncomingMessageHeaders.Action;
if (action.EndsWith("/GetRoles"))
{
// 解析訊息內容,檢查參數與身分
var buffer = ctx.RequestContext.RequestMessage.CreateBufferedCopy(int.MaxValue);
var msg = buffer.CreateMessage();
var nav = buffer.CreateNavigator();
// XPath 提取參數...
string username = nav.SelectSingleNode("//username", nsmgr).Value;
return ctx.ServiceSecurityContext.PrimaryIdentity.Name == username
|| ctx.GetPrincipal().IsInRole("Administrators");
}
return true;
}
}
FindClaims/ContainsClaim 查詢ServiceSecurityContext.Current.AuthorizationContext 存取Evaluate 方法中轉換技術性聲明為業務聲明public class CustomerAuthPolicy : IAuthorizationPolicy
{
public bool Evaluate(EvaluationContext ctx, ref object state)
{
// 1. 取得技術性身分聲明
Claim idClaim = ctx.ClaimSets.FindIdentityClaim();
// 2. 映射為業務使用者 ID
string userId = MapToUserId(idClaim);
// 3. 查詢業務屬性並建構新 ClaimSet
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, userId, Rights.Identity),
new Claim("https://myschema/purchaselimit", GetPurchaseLimit(userId), Rights.PossessProperty),
new Claim(ClaimTypes.Email, GetEmail(userId), Rights.PossessProperty)
};
ctx.AddClaimSet(this, new DefaultClaimSet(ClaimSet.System, claims));
return true;
}
}
public void PlaceOrder(Order order)
{
int limit = GetPurchaseLimitFromClaims();
if (order.Total > limit) throw new FaultException("超過採購額度");
}
private int GetPurchaseLimitFromClaims()
{
var ctx = ServiceSecurityContext.Current.AuthorizationContext;
foreach (var set in ctx.ClaimSets)
{
foreach (var claim in set.FindClaims("https://myschema/purchaselimit", Rights.PossessProperty))
{
return int.Parse(claim.Resource.ToString());
}
}
throw new Exception("缺少採購額度聲明");
}
SecurityTokenService 基類僅供教學/原型WCF 授權模型隨系統複雜度演進:角色基礎解決「是誰、能做什麼」,聲明基礎解決「攜帶什麼屬性、跨域怎麼信任」。掌握 IAuthorizationPolicy 聲明轉換管線與 ServiceAuthorizationManager 集中攔截,是構建企業級安全架構的關鍵。STS 將授權邏輯進一步外部化,為雲原生與零信任架構奠基。