"Add SAML" 在 AD FS 2.0 管理器上被禁用
"Add SAML" is disabled on AD FS 2.0 manager
当我在 AD FS 管理器上手动添加依赖方时,我能够更新 "Endpoints" 并设置 SAML 注销,但是当我将提供 URL 的 RP 添加到 federatedmetadata.xml 文件并且创建 RP 后,AD FS 管理器不允许我将 SAML 注销 link 添加到我新创建的 RP。
我想如果我必须只使用 URL 到 XML 配置我的 RP,那么添加 SAML 注销的唯一方法是将它包含在 federatedmetadata.xml 中,但我不知道如何配置包含单点退出 link 的 XML ,最让我担心的是我无法做到这一点,因为如果我更改我的 XML 手动它将变得不可用,因为一旦您更改 XML.
AD FS 管理器就会开始抛出异常
当 AD FS 管理器完全禁用此类选项时,如何在 AD FS 2.0 上为我的 RP 添加 SAML 注销?
当您手动添加 RP 信任时,是否勾选了 "Enable support for SAML" 以及 "Enable support for "WS-Fed" 的复选框?
当您这样做时,两个 "Add" 框都会启用。
为了允许管理端点,同时在 AD FS 上设置 RP,URL 指向 FederationMetadata.xml,我必须创建自己的 FederationMetadata.xml,其中包含所有必要的端点、签名、加密等。在此之前,我使用了 FedAuthUtil,但它并没有完成所有工作。
非常感谢 Evan Larsen 的 blog post,他完成了几乎所有脏活,尽管必须应用一些更改。
public class FederationMetadataHandler : IHttpHandler
{
private object criticalSection = new object();
private volatile EntityDescriptor entity;
private string relyingPartyUrl = WebConfigurationManager.AppSettings[""];
private string relyingPartySignOutUrl = WebConfigurationManager.AppSettings[""];
private string signingCertificateThumbprint = WebConfigurationManager.AppSettings[""];
private string encryptionCertificateThumbprint = WebConfigurationManager.AppSettings[""];
public FederationMetadataHandler() { }
// This tells the website that this service is re-usable
// so it does not need to run the code everytime. It will keep an instances of this
// class and just return the FederationMetadata.xml file once its been processed once
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
EnsureInitialized();
context.Response.Clear();
context.Response.ContentType = "text/xml";
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
MetadataSerializer serializer = new MetadataSerializer();
serializer.WriteMetadata(context.Response.OutputStream, entity);
}
private void EnsureInitialized()
{
if (entity == null)
{
lock (criticalSection)
{
if (entity == null)
{
HttpContext context = HttpContext.Current;
Uri baseUri = new Uri(new Uri(context.Request.Url.AbsoluteUri), context.Request.ApplicationPath);
ApplicationServiceDescriptor sts = new ApplicationServiceDescriptor();
SingleSignOnDescriptor sso = new SingleSignOnDescriptor();
ServiceProviderSingleSignOnDescriptor spsso = new ServiceProviderSingleSignOnDescriptor();
FillRequestedClaimTypes(sts.ClaimTypesRequested);
FillEndPoints(sts);
FillSupportedProtocols(sts);
FillSigningKey(sts);
FillEncryptionKey(sts);
FillSingleSignOutEndPoints(spsso);
EntityId id = new EntityId(this.relyingPartyUrl);
entity = new EntityDescriptor(id);
entity.SigningCredentials = GetSigningCredentials();
entity.RoleDescriptors.Add(sts);
entity.RoleDescriptors.Add(spsso);
}
}
}
}
private void FillRequestedClaimTypes(ICollection<DisplayClaim> claimTypes)
{
// possibly make this read from a config file in the future..
//ClaimsProvider.GetDisplayClaims().ForEach(dc => claimTypes.Add(dc));
DisplayClaim name = new DisplayClaim(ClaimTypes.Name);
claimTypes.Add(name);
}
private void FillEndPoints(ApplicationServiceDescriptor sts)
{
EndpointAddress activeEndpoint = new EndpointAddress(this.relyingPartyUrl);
sts.TargetScopes.Add(activeEndpoint);
sts.PassiveRequestorEndpoints.Add(activeEndpoint);
}
private void FillSupportedProtocols(ApplicationServiceDescriptor sts)
{
sts.ProtocolsSupported.Add(new Uri(WSFederationConstants.Namespace));
}
private void FillSigningKey(ApplicationServiceDescriptor sts)
{
KeyDescriptor key = GetKeyDescriptor(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, signingCertificateThumbprint, KeyType.Signing);
sts.Keys.Add(key);
}
private void FillEncryptionKey(ApplicationServiceDescriptor sts)
{
KeyDescriptor key = GetKeyDescriptor(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, encryptionCertificateThumbprint, KeyType.Encryption);
sts.Keys.Add(key);
}
private void FillSingleSignOutEndPoints(ServiceProviderSingleSignOnDescriptor spsso)
{
spsso.ProtocolsSupported.Add(new Uri("urn:oasis:names:tc:SAML:1.1:protocol"));
spsso.ProtocolsSupported.Add(new Uri("urn:oasis:names:tc:SAML:2.0:protocol"));
spsso.AssertionConsumerService.Add(0, new IndexedProtocolEndpoint(0, new Uri("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"), new Uri(relyingPartyUrl)));
spsso.SingleLogoutServices.Add(new ProtocolEndpoint(new Uri("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"), new Uri(relyingPartySignOutUrl)));
}
private Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials GetSigningCredentials()
{
X509Certificate2 certificate = GetCertificate(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, encryptionCertificateThumbprint);
if (certificate == null) throw new Exception("Signing certificate is not found");
return new Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials(certificate);
}
private KeyDescriptor GetKeyDescriptor(StoreName storeName, StoreLocation storeLocation, X509FindType findByType, object findByValue, KeyType keyType)
{
X509Certificate2 certificate = GetCertificate(storeName, storeLocation, findByType, findByValue);
if (certificate == null) throw new Exception(string.Format(@"Certificate for type \""{0}\"" is not found", findByType));
X509RawDataKeyIdentifierClause clause = new X509SecurityToken(certificate).CreateKeyIdentifierClause<X509RawDataKeyIdentifierClause>();
KeyDescriptor key = new KeyDescriptor(new SecurityKeyIdentifier(clause));
key.Use = keyType;
return key;
}
private X509Certificate2 GetCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findBy, object findValue)
{
X509Certificate2 certificate = null;
X509Store store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection matchedCertificates = store.Certificates.Find(findBy, findValue, false);
if (matchedCertificates.Count > 0) certificate = matchedCertificates[0];
return certificate;
}
}
当我在 AD FS 管理器上手动添加依赖方时,我能够更新 "Endpoints" 并设置 SAML 注销,但是当我将提供 URL 的 RP 添加到 federatedmetadata.xml 文件并且创建 RP 后,AD FS 管理器不允许我将 SAML 注销 link 添加到我新创建的 RP。
我想如果我必须只使用 URL 到 XML 配置我的 RP,那么添加 SAML 注销的唯一方法是将它包含在 federatedmetadata.xml 中,但我不知道如何配置包含单点退出 link 的 XML ,最让我担心的是我无法做到这一点,因为如果我更改我的 XML 手动它将变得不可用,因为一旦您更改 XML.
AD FS 管理器就会开始抛出异常当 AD FS 管理器完全禁用此类选项时,如何在 AD FS 2.0 上为我的 RP 添加 SAML 注销?
当您手动添加 RP 信任时,是否勾选了 "Enable support for SAML" 以及 "Enable support for "WS-Fed" 的复选框?
当您这样做时,两个 "Add" 框都会启用。
为了允许管理端点,同时在 AD FS 上设置 RP,URL 指向 FederationMetadata.xml,我必须创建自己的 FederationMetadata.xml,其中包含所有必要的端点、签名、加密等。在此之前,我使用了 FedAuthUtil,但它并没有完成所有工作。
非常感谢 Evan Larsen 的 blog post,他完成了几乎所有脏活,尽管必须应用一些更改。
public class FederationMetadataHandler : IHttpHandler
{
private object criticalSection = new object();
private volatile EntityDescriptor entity;
private string relyingPartyUrl = WebConfigurationManager.AppSettings[""];
private string relyingPartySignOutUrl = WebConfigurationManager.AppSettings[""];
private string signingCertificateThumbprint = WebConfigurationManager.AppSettings[""];
private string encryptionCertificateThumbprint = WebConfigurationManager.AppSettings[""];
public FederationMetadataHandler() { }
// This tells the website that this service is re-usable
// so it does not need to run the code everytime. It will keep an instances of this
// class and just return the FederationMetadata.xml file once its been processed once
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
EnsureInitialized();
context.Response.Clear();
context.Response.ContentType = "text/xml";
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
MetadataSerializer serializer = new MetadataSerializer();
serializer.WriteMetadata(context.Response.OutputStream, entity);
}
private void EnsureInitialized()
{
if (entity == null)
{
lock (criticalSection)
{
if (entity == null)
{
HttpContext context = HttpContext.Current;
Uri baseUri = new Uri(new Uri(context.Request.Url.AbsoluteUri), context.Request.ApplicationPath);
ApplicationServiceDescriptor sts = new ApplicationServiceDescriptor();
SingleSignOnDescriptor sso = new SingleSignOnDescriptor();
ServiceProviderSingleSignOnDescriptor spsso = new ServiceProviderSingleSignOnDescriptor();
FillRequestedClaimTypes(sts.ClaimTypesRequested);
FillEndPoints(sts);
FillSupportedProtocols(sts);
FillSigningKey(sts);
FillEncryptionKey(sts);
FillSingleSignOutEndPoints(spsso);
EntityId id = new EntityId(this.relyingPartyUrl);
entity = new EntityDescriptor(id);
entity.SigningCredentials = GetSigningCredentials();
entity.RoleDescriptors.Add(sts);
entity.RoleDescriptors.Add(spsso);
}
}
}
}
private void FillRequestedClaimTypes(ICollection<DisplayClaim> claimTypes)
{
// possibly make this read from a config file in the future..
//ClaimsProvider.GetDisplayClaims().ForEach(dc => claimTypes.Add(dc));
DisplayClaim name = new DisplayClaim(ClaimTypes.Name);
claimTypes.Add(name);
}
private void FillEndPoints(ApplicationServiceDescriptor sts)
{
EndpointAddress activeEndpoint = new EndpointAddress(this.relyingPartyUrl);
sts.TargetScopes.Add(activeEndpoint);
sts.PassiveRequestorEndpoints.Add(activeEndpoint);
}
private void FillSupportedProtocols(ApplicationServiceDescriptor sts)
{
sts.ProtocolsSupported.Add(new Uri(WSFederationConstants.Namespace));
}
private void FillSigningKey(ApplicationServiceDescriptor sts)
{
KeyDescriptor key = GetKeyDescriptor(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, signingCertificateThumbprint, KeyType.Signing);
sts.Keys.Add(key);
}
private void FillEncryptionKey(ApplicationServiceDescriptor sts)
{
KeyDescriptor key = GetKeyDescriptor(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, encryptionCertificateThumbprint, KeyType.Encryption);
sts.Keys.Add(key);
}
private void FillSingleSignOutEndPoints(ServiceProviderSingleSignOnDescriptor spsso)
{
spsso.ProtocolsSupported.Add(new Uri("urn:oasis:names:tc:SAML:1.1:protocol"));
spsso.ProtocolsSupported.Add(new Uri("urn:oasis:names:tc:SAML:2.0:protocol"));
spsso.AssertionConsumerService.Add(0, new IndexedProtocolEndpoint(0, new Uri("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"), new Uri(relyingPartyUrl)));
spsso.SingleLogoutServices.Add(new ProtocolEndpoint(new Uri("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"), new Uri(relyingPartySignOutUrl)));
}
private Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials GetSigningCredentials()
{
X509Certificate2 certificate = GetCertificate(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, encryptionCertificateThumbprint);
if (certificate == null) throw new Exception("Signing certificate is not found");
return new Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials(certificate);
}
private KeyDescriptor GetKeyDescriptor(StoreName storeName, StoreLocation storeLocation, X509FindType findByType, object findByValue, KeyType keyType)
{
X509Certificate2 certificate = GetCertificate(storeName, storeLocation, findByType, findByValue);
if (certificate == null) throw new Exception(string.Format(@"Certificate for type \""{0}\"" is not found", findByType));
X509RawDataKeyIdentifierClause clause = new X509SecurityToken(certificate).CreateKeyIdentifierClause<X509RawDataKeyIdentifierClause>();
KeyDescriptor key = new KeyDescriptor(new SecurityKeyIdentifier(clause));
key.Use = keyType;
return key;
}
private X509Certificate2 GetCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findBy, object findValue)
{
X509Certificate2 certificate = null;
X509Store store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection matchedCertificates = store.Certificates.Find(findBy, findValue, false);
if (matchedCertificates.Count > 0) certificate = matchedCertificates[0];
return certificate;
}
}