如何让自定义STS接收SAML令牌作为客户端凭据完成身份认证?
我之前做过类似的STS链式签发场景,刚好可以给你一套可行的方案,核心是把自定义STS配置成接收SAML 2.0传入令牌,验证后再签发自有令牌给下游WCF服务。下面是具体步骤和示例:
一、先搞定自定义STS的绑定配置:接受SAML 2.0 IssuedToken
自定义STS本质是一个WCF服务,需要用ws2007FederationHttpBinding来适配联邦身份场景,同时指定传入令牌类型为SAML 2.0。修改你的STS的web.config:
<system.serviceModel> <bindings> <ws2007FederationHttpBinding> <binding name="Saml2InboundBinding"> <security mode="Message"> <message establishSecurityContext="false"> <!-- 指定传入令牌是SAML 2.0 --> <issuedTokenParameters tokenType="urn:oasis:names:tc:SAML:2.0:assertion"> <!-- 配置信任的外部IdP元数据,自动获取证书和端点 --> <issuerMetadata address="https://your-external-idp/FederationMetadata/2007-06/FederationMetadata.xml" /> <!-- 手动指定IdP的签名证书,确保验证可信 --> <issuerCertificate findValue="IdP_CERT_THUMBPRINT" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindByThumbprint" /> </issuedTokenParameters> </message> </security> </binding> </ws2007FederationHttpBinding> </bindings> <services> <service name="YourNamespace.CustomSecurityTokenService"> <!-- 端点使用上面配置的绑定,契约用WIF的IWSTrust13SyncContract --> <endpoint address="STS" binding="ws2007FederationHttpBinding" bindingConfiguration="Saml2InboundBinding" contract="Microsoft.IdentityModel.SecurityTokenService.IWSTrust13SyncContract" /> </service> </services> <!-- WIF核心配置:信任IdP、验证证书 --> <microsoft.identityModel> <service> <audienceUris> <!-- 自定义STS的受众地址,确保SAML令牌的Audience匹配 --> <add value="https://your-custom-sts/STS" /> </audienceUris> <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel"> <trustedIssuers> <!-- 信任的外部IdP证书和名称 --> <add thumbprint="IdP_CERT_THUMBPRINT" name="https://your-external-idp/" /> </trustedIssuers> </issuerNameRegistry> <certificateValidation certificateValidationMode="PeerOrChainTrust" /> </service> </microsoft.identityModel> </system.serviceModel>
二、自定义STS的代码逻辑:验证SAML令牌+生成自有令牌
你的自定义STS需要继承SecurityTokenService,重写两个核心方法:GetScope(确定依赖方信任范围)和GetOutputClaimsIdentity(处理传入声明并生成输出声明)。
示例代码:
using Microsoft.IdentityModel.Claims; using Microsoft.IdentityModel.SecurityTokenService; using System.Linq; namespace YourNamespace { public class CustomSecurityTokenService : SecurityTokenService { public CustomSecurityTokenService(SecurityTokenServiceConfiguration config) : base(config) { } protected override Scope GetScope(ClaimsPrincipal incomingPrincipal, RequestSecurityToken request) { // 验证依赖方(WCF服务)是否在信任列表里 var relyingPartyUri = request.AppliesTo.Uri; if (!IsTrustedRelyingParty(relyingPartyUri)) { throw new InvalidRequestException($"依赖方 {relyingPartyUri} 不受信任"); } // 创建Scope:指定依赖方地址、STS的签名凭据 var scope = new Scope(relyingPartyUri, Configuration.SigningCredentials); // 指定你要签发的自定义令牌类型,必须和下游WCF服务配置一致 scope.TokenType = "urn:your-custom-sts:token:v1"; // 可选:如果需要加密令牌,设置依赖方的加密证书 // scope.EncryptingCredentials = new X509EncryptingCredentials(GetRelyingPartyCertificate(relyingPartyUri)); return scope; } protected override ClaimsIdentity GetOutputClaimsIdentity(ClaimsPrincipal incomingPrincipal, RequestSecurityToken request, Scope scope) { // 从传入的SAML令牌中获取用户声明 var incomingClaims = incomingPrincipal.Claims; // 1. 核心验证:确保关键声明存在,比如用户ID、令牌未过期等 var userIdClaim = incomingClaims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier); if (userIdClaim == null) { throw new InvalidRequestException("SAML令牌缺少用户标识声明"); } // 2. 声明转换:根据下游WCF服务的需求,过滤或转换声明 var outputClaims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, userIdClaim.Value), // 可以添加自定义声明,比如从SAML令牌提取角色,或者添加WCF需要的权限声明 new Claim("http://your-custom-sts/claims/wcf-access", "Allowed"), // 保留SAML令牌中的其他必要声明,比如姓名、邮箱等 incomingClaims.FirstOrDefault(c => c.Type == ClaimTypes.Name) }.Where(c => c != null).ToList(); // 返回要嵌入到自定义STS令牌中的身份 return new ClaimsIdentity(outputClaims, scope.TokenType); } // 辅助方法:验证依赖方是否可信 private bool IsTrustedRelyingParty(Uri relyingPartyUri) { // 这里可以从配置或数据库中读取信任的依赖方列表 return relyingPartyUri.AbsoluteUri == "https://your-wcf-service/"; } } }
三、下游WCF服务的配置:信任自定义STS的令牌
WCF服务需要配置成接受自定义STS签发的令牌,同样用ws2007FederationHttpBinding:
<system.serviceModel> <bindings> <ws2007FederationHttpBinding> <binding name="CustomSTSTokenBinding"> <security mode="Message"> <message establishSecurityContext="false"> <issuedTokenParameters tokenType="urn:your-custom-sts:token:v1"> <!-- 指定自定义STS的地址 --> <issuer address="https://your-custom-sts/STS" binding="ws2007FederationHttpBinding" /> <!-- STS的元数据地址,自动获取配置 --> <issuerMetadata address="https://your-custom-sts/FederationMetadata/2007-06/FederationMetadata.xml" /> <!-- 信任STS的签名证书 --> <issuerCertificate findValue="YOUR_STS_CERT_THUMBPRINT" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindByThumbprint" /> </issuedTokenParameters> </message> </security> </binding> </ws2007FederationHttpBinding> </bindings> <services> <service name="YourWcfNamespace.WcfService"> <endpoint address="" binding="ws2007FederationHttpBinding" bindingConfiguration="CustomSTSTokenBinding" contract="YourWcfNamespace.IWcfService" /> </service> </services> <microsoft.identityModel> <service> <audienceUris> <add value="https://your-wcf-service/" /> </audienceUris> <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel"> <trustedIssuers> <add thumbprint="YOUR_STS_CERT_THUMBPRINT" name="https://your-custom-sts/" /> </trustedIssuers> </issuerNameRegistry> </service> </microsoft.identityModel> </system.serviceModel>
四、几个必须注意的点
- 证书信任链:所有参与方的证书(外部IdP、自定义STS、WCF服务)都要放到对应的信任存储(比如LocalMachine/TrustedPeople),否则令牌验证会失败。
- 令牌类型一致性:自定义STS签发的
tokenType必须和WCF服务配置的完全一致,大小写都不能错。 - 声明验证要严格:在
GetOutputClaimsIdentity里一定要验证SAML令牌的有效性,比如检查令牌过期时间、受众是否匹配自定义STS的地址,防止恶意令牌。 - 元数据维护:确保自定义STS和外部IdP都发布联邦元数据,这样配置可以自动更新,减少手动维护成本。
内容的提问来源于stack exchange,提问作者ANewGuyInTown
相关产品推荐
相关产品推荐

