MVC 5应用中Sustainsys.Saml2多租户实现及相关问题咨询
多租户下Sustainsys.Saml2配置与问题解决
一、添加多租户身份提供者并在SignIn时指定IDP
1. 配置多个身份提供者
在web.config的<identityProviders>节点下添加多个<add>元素,每个对应一个租户的身份提供者(IDP),注意每个IDP的entityId需唯一:
<sustainsys.saml2 entityId="https://your-app.com/tenant1" returnUrl="https://your-app.com/tenant1" publicOrigin="https://your-app.com"> <identityProviders> <!-- 租户1的IDP --> <add entityId="https://idp-tenant1.com/Metadata" signOnUrl="https://idp-tenant1.com/" allowUnsolicitedAuthnResponse="true" binding="HttpPost" metadataLocation="~/App_Data/Tenant1Metadata.xml" wantAuthnRequestsSigned="true"> <signingCertificate storeName="AddressBook" storeLocation="LocalMachine" findValue="TENANT1CERT" x509FindType="FindBySerialNumber" /> </add> <!-- 租户2的IDP --> <add entityId="https://idp-tenant2.com/Metadata" signOnUrl="https://idp-tenant2.com/" allowUnsolicitedAuthnResponse="true" binding="HttpPost" metadataLocation="~/App_Data/Tenant2Metadata.xml" wantAuthnRequestsSigned="true"> <signingCertificate storeName="AddressBook" storeLocation="LocalMachine" findValue="TENANT2CERT" x509FindType="FindBySerialNumber" /> </add> </identityProviders> </sustainsys.saml2>
2. 在SignIn动作中指定IDP
修改SignIn方法,接收idp参数(对应目标IDP的entityId),将其传入请求数据中,Sustainsys.Saml2会自动匹配对应的IDP:
public ActionResult SignIn(string idp) { var requestData = Request.ToHttpRequestData(); // 传入指定的IDP标识 if (!string.IsNullOrEmpty(idp)) { requestData.QueryString.Add("idp", idp); } var result = CommandFactory.GetCommand(CommandFactory.SignInCommandName).Run(requestData, Options); if (result.HandledResult) { throw new NotSupportedException("The MVC controller doesn't support setting CommandResult.HandledResult."); } result.ApplyCookies(Response, Options.Notifications.EmitSameSiteNone(Request.UserAgent)); return result.ToActionResult(); }
3. 多租户动态SP配置(可选)
如果每个租户需要独立的SP实体ID(entityId),不能使用静态的Options.FromConfiguration,需根据当前租户动态构建Options:
private IOptions GetTenantOptions(string tenantId) { var options = new Sustainsys.Saml2.Configuration.Options(); // 根据租户ID加载对应的SP配置 options.SPOptions.EntityId = new EntityId($"https://your-app.com/{tenantId}"); options.SPOptions.ReturnUrl = new Uri($"https://your-app.com/{tenantId}"); options.SPOptions.PublicOrigin = new Uri($"https://your-app.com"); // 加载该租户对应的IDP配置(可从数据库、配置文件读取) var idp = new IdentityProvider(new EntityId($"https://idp-{tenantId}.com/Metadata"), options.SPOptions) { SignOnUrl = new Uri($"https://idp-{tenantId}.com/"), AllowUnsolicitedAuthnResponse = true, Binding = Saml2Binding.HttpPost, WantAuthnRequestsSigned = true }; // 添加签名证书 idp.SigningKeys.AddConfiguredKey(new X509CertificateStoreConfig { StoreName = StoreName.AddressBook, StoreLocation = StoreLocation.LocalMachine, FindValue = $"TENANT{tenantId}CERT", X509FindType = X509FindType.FindBySerialNumber }); options.IdentityProviders.Add(idp); return options; }
在SignIn动作中调用此方法获取对应租户的Options:
public ActionResult SignIn(string tenantId, string idp) { var options = GetTenantOptions(tenantId); var requestData = Request.ToHttpRequestData(); if (!string.IsNullOrEmpty(idp)) { requestData.QueryString.Add("idp", idp); } var result = CommandFactory.GetCommand(CommandFactory.SignInCommandName).Run(requestData, options); // 后续代码不变 }
二、解决RelayData在Acs动作中为null的问题
1. 正确设置RelayData的时机
确保在ApplyCookies之前设置RelayData,因为ApplyCookies会将RelayData写入临时Cookie,供ACS动作读取:
public ActionResult SignIn(string idp) { var requestData = Request.ToHttpRequestData(); if (!string.IsNullOrEmpty(idp)) { requestData.QueryString.Add("idp", idp); } var result = CommandFactory.GetCommand(CommandFactory.SignInCommandName).Run(requestData, Options); if (result.HandledResult) { throw new NotSupportedException("The MVC controller doesn't support setting CommandResult.HandledResult."); } // 在ApplyCookies之前设置RelayData result.RelayData = new Dictionary<string, string> { { "ABC", "123" } }; result.ApplyCookies(Response, Options.Notifications.EmitSameSiteNone(Request.UserAgent)); return result.ToActionResult(); }
2. 在ACS动作中读取RelayData
确保ACS动作正确处理CommandResult并读取RelayData:
public ActionResult Acs() { var result = CommandFactory.GetCommand(CommandFactory.AcsCommandName).Run(Request.ToHttpRequestData(), Options); // 读取RelayData if (result.RelayData.TryGetValue("ABC", out string abcValue)) { // 使用自定义数据abcValue } result.ApplyCookies(Response, Options.Notifications.EmitSameSiteNone(Request.UserAgent)); return result.ToActionResult(); }
3. 排查Cookie问题
如果仍为null,检查以下几点:
- 确保Cookie策略允许写入RelayData的临时Cookie(注意SameSite、Secure属性)
- 确认请求在同一域名下(跨域会导致Cookie无法传递)
- 可手动指定
RelayDataCookie的配置:
options.RelayDataCookie.Name = "Saml2RelayData"; options.RelayDataCookie.Secure = true; options.RelayDataCookie.SameSite = SameSiteMode.Lax;
内容的提问来源于stack exchange,提问作者centralscru
相关产品推荐
相关产品推荐

