【问题标题】:How to validate ADFS SAML token如何验证 ADFS SAML 令牌
【发布时间】:2013-09-09 15:23:49
【问题描述】:

我目前正在像这样从 ADFS 生成 SAML 令牌:

 WSTrustChannelFactory factory = null;
        try
        {
            // use a UserName Trust Binding for username authentication
            factory = new WSTrustChannelFactory(
                new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
                 new EndpointAddress("https://adfs.company.com/adfs/services/trust/13/usernamemixed"));

            factory.TrustVersion = TrustVersion.WSTrust13;

            factory.Credentials.UserName.UserName = "user";
            factory.Credentials.UserName.Password = "pw";


            var rst = new RequestSecurityToken
            {
                RequestType = RequestTypes.Issue,
                AppliesTo = new EndpointReference(relyingPartyId),
                KeyType = KeyTypes.Bearer
            };
            IWSTrustChannelContract channel = factory.CreateChannel();
              GenericXmlSecurityToken genericToken = channel.Issue(rst) 
               as     GenericXmlSecurityToken;
         }
        finally
        {
            if (factory != null)
            {
                try
                {
                    factory.Close();
                }
                catch (CommunicationObjectFaultedException)
                {
                    factory.Abort();
                }
            }
        }

现在假设我构建了一个使用这些令牌进行身份验证的 Web 应用程序。据我所知,工作流程应该是这样的:

  • 生成令牌
  • 客户端获取生成的令牌(有效登录后)
  • 客户端缓存令牌
  • 客户端使用令牌进行下次登录
  • Web 应用程序验证令牌,不必调用 ADFS

如何验证客户端提供的令牌是否有效?我需要 ADFS 服务器的证书来解密令牌吗?

【问题讨论】:

  • 不过,我总是可以将令牌保存到数据库中......

标签: c# validation saml adfs


【解决方案1】:

在查看了优秀的thinktecture身份服务器代码(https://github.com/thinktecture/Thinktecture.IdentityServer.v2/tree/master/src/Libraries/Thinktecture.IdentityServer.Protocols/AdfsIntegration)后,我提取了解决方案:

using Newtonsoft.Json;
using System;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Text;
using System.Xml;
using Thinktecture.IdentityModel.Extensions;
using Thinktecture.IdentityModel.WSTrust;

namespace SimpleWebConsole
{
internal class ADFS
{
    public static void tokenTest()
    {
        string relyingPartyId = "https://party.mycomp.com";
        WSTrustChannelFactory factory = null;
        try
        {
            // use a UserName Trust Binding for username authentication
            factory = new WSTrustChannelFactory(
                new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
                 new EndpointAddress("https://adfs.mycomp.com/adfs/services/trust/13/usernamemixed"));

            factory.TrustVersion = TrustVersion.WSTrust13;

            factory.Credentials.UserName.UserName = "test";
            factory.Credentials.UserName.Password = "test";

            var rst = new RequestSecurityToken
            {
                RequestType = RequestTypes.Issue,
                AppliesTo = new EndpointReference(relyingPartyId),
                KeyType = KeyTypes.Bearer
            };
            IWSTrustChannelContract channel = factory.CreateChannel();
            GenericXmlSecurityToken genericToken = channel.Issue(rst) as GenericXmlSecurityToken; //MessageSecurityException -> PW falsch

            var _handler = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
            var tokenString = genericToken.ToTokenXmlString();

            var samlToken2 = _handler.ReadToken(new XmlTextReader(new StringReader(tokenString)));

            ValidateSamlToken(samlToken2);

            X509Certificate2 certificate = null;

            X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadOnly);
            certificate = store.Certificates.Find(X509FindType.FindByThumbprint, "thumb", false)[0];

            var jwt=ConvertSamlToJwt(samlToken2, "https://party.mycomp.com", certificate);

        }
        finally
        {
            if (factory != null)
            {
                try
                {
                    factory.Close();
                }
                catch (CommunicationObjectFaultedException)
                {
                    factory.Abort();
                }
            }
        }
    }

    public static TokenResponse ConvertSamlToJwt(SecurityToken securityToken, string scope, X509Certificate2 SigningCertificate)
    {
        var subject = ValidateSamlToken(securityToken);


        var descriptor = new SecurityTokenDescriptor
        {
            Subject = subject,
            AppliesToAddress = scope,
            SigningCredentials = new X509SigningCredentials(SigningCertificate),
            TokenIssuerName = "https://panav.mycomp.com",
            Lifetime = new Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddMinutes(10080))
        };


        var jwtHandler = new JwtSecurityTokenHandler();
        var jwt = jwtHandler.CreateToken(descriptor);


        return new TokenResponse
        {
            AccessToken = jwtHandler.WriteToken(jwt),
            ExpiresIn = 10080
        };
    }


    public static ClaimsIdentity ValidateSamlToken(SecurityToken securityToken)
    {
        var configuration = new SecurityTokenHandlerConfiguration();
        configuration.AudienceRestriction.AudienceMode = AudienceUriMode.Never;
        configuration.CertificateValidationMode = X509CertificateValidationMode.None;
        configuration.RevocationMode = X509RevocationMode.NoCheck;
        configuration.CertificateValidator = X509CertificateValidator.None;

        var registry = new ConfigurationBasedIssuerNameRegistry();
        registry.AddTrustedIssuer("thumb", "ADFS Signing - mycomp.com");
        configuration.IssuerNameRegistry = registry;

        var handler = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection(configuration);
        var identity = handler.ValidateToken(securityToken).First();
        return identity;
    }

    public class TokenResponse
    {
        [JsonProperty(PropertyName = "access_token")]
        public string AccessToken { get; set; }


        [JsonProperty(PropertyName = "token_type")]
        public string TokenType { get; set; }


        [JsonProperty(PropertyName = "expires_in")]
        public int ExpiresIn { get; set; }


        [JsonProperty(PropertyName = "refresh_token")]
        public string RefreshToken { get; set; }
    }

}
}

【讨论】:

  • 我还添加了一些代码将令牌转换为 JWT,以便可以轻松地在 Web 请求中使用。
  • 是您需要存储在本地用于解密/读取令牌的 ADFS 令牌签名证书吗?
【解决方案2】:

简单多了!对于使用 WIF 的网站(假设您使用的是 .NET),然后将应用程序与 ADFS 联合。 (WIF SDK 中包含一个向导)。一切都得到照顾。解析、验证等由框架完成。您的应用会以常规方式与用户打交道:this.User.Namethis.User.IsInRole("admin") 等。

该场景记录在here

【讨论】:

  • web api 怎么样?没有 cookie,只有 HTTP 标头
  • 取决于谁调用 API 以及 API 是如何实现的。对于 SOAP 服务,您可以使用 SAML 和类似于您的示例的调用。对于 REST 服务,您不会经常使用 SAML,而是使用更轻量级的东西。如果您扩展整个场景,那就太好了。
猜你喜欢
  • 2011-04-25
  • 1970-01-01
  • 2016-01-19
  • 2017-01-12
  • 2015-05-19
  • 2017-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多