WS-Federation登录 Asp.NET 5 MVC 6 ADFS

10

你好,我一直在尝试在我的MVC6 Web应用程序上实现WS-Fed SSO,我已经阅读了一些关于认证和识别我的要求的内容。我必须使用WsFederationAuth,因此OAuth或SAML协议对我来说都不起作用。

编辑:根据@Pinpoint的建议,我尝试使用OWIN中间件来实现连接,但我将使用完整框架DNX451而不是DNXCore,这是在等待Vnext支持ws-fed的过程中进行的尝试。

Pinpoint适配器扩展

 public static class AppBuilderExtensions
    {
#if !DNXCORE50
        public static IApplicationBuilder UseOwinAppBuilder(this IApplicationBuilder app,
            Action<IAppBuilder> configuration)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }

            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }


            return app.UseOwin(setup => setup(next =>
            {
                var builder = new AppBuilder();
                var lifetime = (IApplicationLifetime) app.ApplicationServices.GetService(typeof (IApplicationLifetime));

                var properties = new AppProperties(builder.Properties);
                properties.AppName = app.ApplicationServices.GetApplicationUniqueIdentifier();
                properties.OnAppDisposing = lifetime.ApplicationStopping;
                properties.DefaultApp = next;

                configuration(builder);

                return builder.Build<Func<IDictionary<string, object>, Task>>();
            }));
        }
#endif
    }

并在 startup.cs 中:

    #if !DNXCORE50
            app.UseOwinAppBuilder(owin =>
            {
                owin.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
                {
                    MetadataAddress =
                        "https://mysite.accesscontrol.windows.net/FederationMetadata/2007-06/FederationMetadata.xml",
                    Wtrealm = "http://localhost:62569/",
                    SignInAsAuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
                    AuthenticationType = "adfs",
                    SecurityTokenHandlers = new SecurityTokenHandlerCollection
                    {
                        new EncryptedSecurityTokenHandler
                        {
                            Configuration = new SecurityTokenHandlerConfiguration
                            {
                                IssuerTokenResolver = new X509CertificateStoreTokenResolver(StoreName.My,
                                    StoreLocation.LocalMachine)
                            }
                        },
                        new Saml2SecurityTokenHandler
                        {
                            CertificateValidator = X509CertificateValidator.None,
                            
                        }
                    }
                });
            });
#endif

我感觉离解决方案越来越近,但还没有完成。在处理与ADFS的身份验证后,我遇到了令牌问题。

根据我的理解,我遇到了以下错误:

SecurityTokenValidationException: IDX10201: None of the the SecurityTokenHandlers could read the 'securityToken':

<Assertion ID="_851fc402-2e9c-4ff8-a743-7d65612255b9" IssueInstant="2015-06-22T16:16:03.852Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
    <Issuer>https://mysite.accesscontrol.windows.net/</Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
            <ds:Reference URI="#_851fc402-2e9c-4ff8-a743-7d65612255b9">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <ds:DigestValue>xZdzOnNIG5Ia***********t0feMWIZMLnY=</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>KmuScnZBdxyaAJrfLgB9AYheUdR*****************************Xs4o8R+eMCPdWNsDjhLu500UlCgitYerjpLTTyRRdwvFo8T7LlsXO2yjv3dx83Yn+GthE+FswNRH07yIHF5wo5+/TAwiVzg+9SDbK+Nj84PdLMxwIfALAebf4/ECdpqkWvt2ligzFoQckEgZMRepOcAVfBxfELyJSUDAjnpfJCrlCQip0nykC+5R37X00flIlB563p48veeLIt0JaUdG4bwtQ8OCMg1KeD5gYix9p4E3TQ7QovN0HDoWTurLK/0H01IS73fIe6/k6DBA==</ds:SignatureValue>
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <X509Data>
                <X509Certificate>MIIDSjCCAjagAwIBAgIQrcBhMtovuJ**********************MDExLzAtBgNVBAMTJm1hcmdvY29uc2VpbC5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE1MDYxNjA4MTYzOFoXDTIwMDUzMTIyMDAwMFowMTEvMC0GA1UEAxMmbWFyZ29jb25zZWlsLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpeZseXX1IYTABUOPr7nfIAXc7cXAI0k+vR3qEbvy0UxEhYAkAocQR2qUTPQ8D1v4lPp59tnHKBGJ0eHt9DYm/SyKkfHsWfqsysZx5NHXSJIhy/SgHwpd8b2q1NKxqBRLrdJKyAua+WWza4p/HMFjEVoN/upZtngSqxUKO/oYqy6m7smkz8vwjzpJR8tyqN881XBQzvryiF/i3ZPFQqlCT9263TMcAGPpym9uvxHzFPPx3u8IDz3AQydyHeViaJhiXGic0VEcm6LMn3JLOYqAzJnv8z89NdpsL4ynv1ekytt/8yyza3CnsU1E4tFDj1HU3785aFZ1xm6wr1MUK9VOTAgMBAAGjZjBkMGIGA1UdAQRbMFmAEN1alzwM3lJSHdh4LFl7uxmhMzAxMS8wLQYDVQQDEyZtYXJnb2NvbnNlaWwuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQrcBhMtovuJ9MilbEjMjS7TAJBgUrDgMCHQUAA4IBAQAsQ5jNKvS2fLtqs9oB6DGTXdI5nAli5UyvZUQlnfnMvgYjJKrZu79iMe9iu88cmtwZVifG14SRbVdTjUOzngIWAJ5KAQk5t//wSkwgAS+U6AFYI/mee9NLEvOEhrRbpGUP0oL504OZ9zTDeXmGu2FybRB2TvdTKLaeVsBvwqgP33QFkcuPK50fCGC1l3SecIeyWL5fsiw/2+GuTKHjCaeRqnYBgDTINptc9PGayLPBTjs4UPzbccmaYyuanmTAMZGU0iRoGJYet2uAasT52QvWZqD0NUZbWyR1N8CBf5EIW2S/TrpoOBYNgZQU5n9PRJjTBhESHXjfa8RipC8RXU9o</X509Certificate>
            </X509Data>
        </KeyInfo>
    </ds:Signature>
    <Subject>
        <NameID>***********</NameID>
        <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" />
    </Subject>
    <Conditions NotBefore="2015-06-22T16:16:03.836Z" NotOnOrAfter="2015-06-22T17:16:03.836Z">
        <AudienceRestriction>
            <Audience>http://localhost:62569/</Audience>
        </AudienceRestriction>
    </Conditions>
    <AttributeStatement>
        <Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid">
            <AttributeValue>********************</AttributeValue>
        </Attribute>
        <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name">
            <AttributeValue>************</AttributeValue>
        </Attribute>
        <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname">
            <AttributeValue>G****l</AttributeValue>
        </Attribute>
        <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
            <AttributeValue>L****s</AttributeValue>
        </Attribute>
        <Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider">
            <AttributeValue>https://sts.windows.net/7102feaa-34af-4756-85ce-b0f69766d78d/</AttributeValue>
        </Attribute>
        <Attribute Name="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider">
            <AttributeValue>https://sts.windows.net/7102feaa-34af-4756-85ce-b0f69766d78d/</AttributeValue>
        </Attribute>
    </AttributeStatement>
    <AuthnStatement AuthnInstant="2015-06-22T14:26:14.020Z">
        <AuthnContext>
            <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
        </AuthnContext>
    </AuthnStatement>
</Assertion>
2个回答

3

正如您已经发现的那样,WS-Federation中间件尚未移植到ASP.NET 5,但是不要惊慌,它肯定会被移植:https://twitter.com/blowdart/status/610526268908535808

同时,您可以使用OWIN/Katana 3 WS-Federation中间件在ASP.NET 5应用程序中,配合一个小的IAppBuilder/IApplicationBuilder适配器(例如:https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/vNext/samples/Mvc/Mvc.Server/Extensions/AppBuilderExtensions.cs#L50),但当然,它将无法与dnxcore50兼容。

如果您有最新版本的ADFS,则也可以考虑切换到OAuth2。


1
在这种情况下,ADFS是否是身份提供者? - Shaun Luttin
是的,虽然“身份提供者”是OpenID Connect术语,但它不受ADFS支持(与其云端对应物AAD不同)。在这种情况下,“授权服务器”将是确切的名称。 - Kévin Chalet
非常感谢@Pinpoint,星期一我会研究一下(我是一个非常有动力的实习生,但我的动力也有限:D)。我肯定想使用OAuth2,但我的老板不想在ADFS部分做任何更改,我必须适应现有部分,因此使用ws-federation。我试图告诉他,在我看来,ws-fed是旧技术,而他坚持使用.NET5和所有新东西,所以坚持在项目的一个方面上坚持旧技术是不合理的。但他有点告诉我懒惰,只是想绕过问题... - Lomithrani
@Pinpoint,我仍然需要了解如何处理我的令牌,但目前进展顺利。你知道我可以在哪里查看吗?我得到了“无法从以下安全密钥标识符中解析用于验证签名所需的密钥”。我尝试了一些方法,但我找不到一个地方来解释所有这些内容,以便不只是尝试东西。 - Lomithrani
结果证明,原因是我使用的不是ADFS而是ACS。因此元数据没有足够的信息,我不得不构建自己的令牌处理程序,现在看起来运行良好。 - Lomithrani
显示剩余4条评论

1
我发现了一种方法,可以使用之前的ASP.NET类从ADFS返回的http请求中获取声明,而不会触及web.config文件。希望这段代码对某些人有用:
using Microsoft.AspNet.Http;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Specialized;
using System.IdentityModel.Configuration;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Selectors;
using System.IdentityModel.Services;
using System.IdentityModel.Tokens;
using System.IO;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel.Security;
using System.Text;
using System.Xml;

public class FederationHelper
{
    public ClaimsIdentity GetClaimsIdentityFromResponse(IConfigurationRoot configurationRoot, HttpContext context)
    {
        var absoluteUri = string.Concat(
                    context.Request.Scheme,
                    "://",
                    context.Request.Host.ToUriComponent(),
                    context.Request.PathBase.ToUriComponent(),
                    context.Request.Path.ToUriComponent(),
                    context.Request.QueryString.ToUriComponent());

        var values = new NameValueCollection();

        foreach (var item in context.Request.Query)
        {
            values.Add(item.Key, item.Value);
        }

        foreach (var item in context.Request.Form)
        {
            values.Add(item.Key, item.Value);
        }

        var federation = new FederationSettings
        {
            AudienceUri = "http://contoso",
            Endpoint = "http://sts/ls",
            IssuerName = "http://sts/trust",
            IssuerThumbprint = "[thumbprint]",
            Realm = "https://myapp"
        };

        var identityConfiguration = new IdentityConfiguration(false);
        identityConfiguration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(federation.AudienceUri));

        var issuers = new ConfigurationBasedIssuerNameRegistry();
        issuers.ConfiguredTrustedIssuers.Add(federation.IssuerThumbprint, federation.IssuerName);
        identityConfiguration.IssuerNameRegistry = issuers;

        var tokenHandler = new SamlSecurityTokenHandler();

        tokenHandler.Configuration = new SecurityTokenHandlerConfiguration();
        tokenHandler.Configuration.AudienceRestriction.AudienceMode = AudienceUriMode.Always;
        tokenHandler.Configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(federation.AudienceUri));
        tokenHandler.Configuration.IssuerNameRegistry = identityConfiguration.IssuerNameRegistry;
        tokenHandler.Configuration.CertificateValidationMode = X509CertificateValidationMode.None;
        tokenHandler.Configuration.RevocationMode = X509RevocationMode.NoCheck;
        tokenHandler.Configuration.CertificateValidator = X509CertificateValidator.None;

        var message = (SignInResponseMessage)WSFederationMessage.CreateFromNameValueCollection(new Uri(absoluteUri), values);
        var token = CreateSecurityToken(tokenHandler, identityConfiguration, message);
        var claims = tokenHandler.ValidateToken(token);

        return claims[0];
    }

    public SecurityToken CreateSecurityToken(SamlSecurityTokenHandler handler, IdentityConfiguration configuration, SignInResponseMessage message)
    {
        var quotas = new XmlDictionaryReaderQuotas();

        using (var reader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(message.Result), quotas))
        {
            var serializer = new WSFederationSerializer(reader);
            var context = new WSTrustSerializationContext(configuration.SecurityTokenHandlerCollectionManager);

            var xml = serializer.CreateResponse(message, context).RequestedSecurityToken.SecurityTokenXml.OuterXml;

            return ReadToken(handler, xml, quotas);
        }
    }

    SecurityToken ReadToken(SamlSecurityTokenHandler handler, string xml, XmlDictionaryReaderQuotas quotas)
    {
        using (var reader = new StringReader(xml))
        {
            using (var xmlReader = XmlReader.Create(reader))
            {
                xmlReader.MoveToContent();

                return handler.ReadToken(xmlReader);
            }
        }
    }
}

从我记得的来看,这看起来很像我最后所做的事情。@fiat,你应该检查一下。 - Lomithrani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接