我目前正在从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应用程序。据我所知,工作流程应该是这样的:
如何验证客户端提供的令牌是否有效?我是否需要ADFS服务器的证书来解密令牌?
答案 0 :(得分:8)
在查看优秀的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; }
}
}
}
答案 1 :(得分:1)
这简单得多!对于网站,您使用WIF(假设您使用的是.NET),然后将应用程序与ADFS联合起来。 (WIF SDK中包含一个向导)。一切都得到了照顾。解析,验证等由框架完成。您的应用会以常规方式与用户打交道:this.User.Name
,this.User.IsInRole("admin")
等
该方案记录在案here。