【发布时间】:2015-08-17 14:40:59
【问题描述】:
我们现在工作的项目是使用 SAML 令牌通过 ADFS 单点登录。
该项目应遵循的基本规则如下:
1. 代理使用他\她的凭据登录到 Windows。
2. 代理登录到 Web 应用程序(依赖方)
3. Web 应用程序应重定向到 ADFS 中的 STS(Active Directory 是身份提供者)并使用代理在其 Windows 身份验证(无缝身份验证)中使用的凭据登录。
4.因此STS登录页面不应该出现,用户应该被认证
5. 之后应收到索赔和安全令牌,以便我们授权代理
实际结果:
1.第一次重定向,再次需要认证(IE认证页面和Firefox\Chrome认证页面)。
- 可以对所有类型的域用户进行身份验证,而不仅仅是 Windows 身份验证用户。
- 首次登录sts登录页面后,无需再次认证。但是我们不想要二次认证。仅在 Windows 登录时(仅适用于 IE)。
配置环境:
1.域控制器+ADFS服务器3.0同机(Win2k12R2)
2. Web应用机(Win2k12 + IIS8.5)
3.机器在同一个域
ADFS 配置:
依赖方配置:
Web 应用配置:
身份验证:
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
<connectionStrings>
<add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-TestApp-20150730141753;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-TestApp-20150730141753.mdf" />
</connectionStrings>
<location path="FederationMetadata">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
<system.web>
<authorization>
<deny users="?" />
</authorization>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<pages>
<namespaces>
<add namespace="System.Web.Optimization" />
</namespaces>
<controls><add assembly="Microsoft.AspNet.Web.Optimization.WebForms" namespace="Microsoft.AspNet.Web.Optimization.WebForms" tagPrefix="webopt" /></controls></pages>
<!--<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" defaultUrl="~/" />
</authentication>-->
<profile defaultProvider="DefaultProfileProvider">
<providers>
<add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</profile>
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
<roleManager defaultProvider="DefaultRoleProvider">
<providers>
<add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</roleManager>
<!--
If you are deploying to a cloud environment that has multiple web server instances,
you should change session state mode from "InProc" to "Custom". In addition,
change the connection string named "DefaultConnection" to connect to an instance
of SQL Server (including SQL Azure and SQL Compact) instead of to SQL Server Express.
-->
<sessionState mode="InProc" customProvider="DefaultSessionProvider">
<providers>
<add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
</providers>
</sessionState>
</system.web>
<system.webServer>
<modules>
<!--<add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />-->
<add name="FixedWSFederationAuthenticationModule" type="TestApp.FixedWSFederationAuthenticationModule, TestApp" preCondition="managedHandler" />
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
</modules>
</system.webServer>
<system.identityModel>
<identityConfiguration saveBootstrapContext="true">
<!-- The identity configuration. No name means default configuration which is always used for passive federation scenarios. see federationConfiguration element -->
<audienceUris>
<add value="https://ccsp12.pj12.loc/testapp" />
</audienceUris>
<issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<trustedIssuers>
<add thumbprint="91992FCF8B03FF9BD98A259FE93B92620E9DD89A" name="http://sts.pj12.loc/adfs/services/trust" />
</trustedIssuers>
</issuerNameRegistry>
<certificateValidation certificateValidationMode="None" />
</identityConfiguration>
</system.identityModel>
<system.identityModel.services>
<federationConfiguration> <!-- Configures the WSFederationAuthenticationModule (WSFAM) and the SessionAuthenticationModule (SAM) when using federated authentication through the WS-Federation protocol -->
<cookieHandler requireSsl="false" />
<!-- passiveRedirectEnabled true means that a relaying party (test app) instead of having its own login page, it will redirect to the sts issuer for authentication and the sts will reply to the relaying party -->
<!-- Due to WSFederationAuthenticationModule bug, the relaying party address must be with '/' at the end -->
<wsFederation passiveRedirectEnabled="true" issuer="https://sts.pj12.loc/adfs/ls/" realm="https://ccsp12.pj12.loc/testapp/" reply="https://ccsp12.pj12.loc/testapp/" requireHttps="true" />
</federationConfiguration>
</system.identityModel.services>
<!--<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="DotNetOpenAuth.Core" publicKeyToken="2780ccd10d57b246" />
<bindingRedirect oldVersion="1.0.0.0-4.0.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="DotNetOpenAuth.AspNet" publicKeyToken="2780ccd10d57b246" />
<bindingRedirect oldVersion="1.0.0.0-4.0.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>-->
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
</configuration>
C#代码:
namespace TestApp
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
// local variables
string claimsTypes = string.Empty;
string claimsValues = string.Empty;
string claimsValueTypes = string.Empty;
string claimsSubjectNames = string.Empty;
string claimsIssuers = string.Empty;
// initialize claims and identity
ClaimsPrincipal claimsPrincipal = Thread.CurrentPrincipal as ClaimsPrincipal;
ClaimsIdentity claimsIdentity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
BootstrapContext bootstrapContext =
ClaimsPrincipal.Current.Identities.First().BootstrapContext
as BootstrapContext;
if (claimsPrincipal != null)
{
signedIn.Text = "You are signed in.";
foreach (Claim claim in claimsPrincipal.Claims)
{
claimsTypes = string.Concat(claimsTypes, "; ", claim.Type);
claimsValues = string.Concat(claimsValues, "; ", claim.Value);
claimsValueTypes = string.Concat(claimsValueTypes, "; ", claim.ValueType);
claimsSubjectNames = string.Concat(claimsSubjectNames, "; ", claim.Subject.Name);
claimsIssuers = string.Concat(claimsIssuers, "; ", claim.Issuer);
}
//claims principals
claimType.Text = claimsTypes;
claimValue.Text = claimsValues;
claimValueType.Text = claimsValueTypes;
claimSubjectName.Text = claimsSubjectNames;
claimIssuer.Text = claimsIssuers;
// ClaimsIdentity
isUserAuthenticated.Text = claimsIdentity.IsAuthenticated.ToString();
authenticationType.Text = claimsIdentity.AuthenticationType;
claimName.Text = claimsIdentity.Name;
// Token
// known bug : http://stackoverflow.com/questions/13514553/wif-4-5-bootstrapcontext-security-token-null
SecurityToken token = null;
if (bootstrapContext.SecurityToken != null)
{
token = bootstrapContext.SecurityToken;
}
else if (!bootstrapContext.Token.Equals(string.Empty))
{
var handlers = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.SecurityTokenHandlers;
token = handlers.ReadToken(new XmlTextReader(new StringReader(bootstrapContext.Token)));
}
SamlSecurityToken sst = token as SamlSecurityToken;
tokenId.Text = sst.Id;
tokenAssertionId.Text = sst.Assertion.AssertionId;
tokenIssuer.Text = sst.Assertion.Issuer;
}
else
{
signedIn.Text = "You are not signed in.";
}
}
}
【问题讨论】:
-
您提供的大部分细节都不重要。真正的问题是您的 adfs Web 应用程序不愿意在没有提示输入凭据的情况下进行集成身份验证。首先,这总是只在 ie 中有效,不要指望很容易让 chrome/ff 支持它。其次,您的身份验证配置似乎没问题。此外,您已经在客户端完成了大部分必需的配置步骤。我想到了什么 - 您能否在 adfs 旁边设置一个虚拟 asp.net 站点,该站点也支持集成身份验证,并验证这是 adfs 问题,而不是您服务器上集成身份验证的问题?
-
@Wiktor Zychla 嗨!感谢您的快速评论。 adfs 旁边的虚拟 asp.net 站点是什么意思?创建我自己的sts网站?服务提供商网站?你能分享一下我如何做到这一点的教程的链接,以便我检查一下吗?谢谢!
-
不,只是一个站点,不是 sts 也不是 ip。只是一个测试集成身份验证是否适用于该特定服务器的站点。我已经部署了数十个具有集成身份验证的 adfses,没有出现重大问题,因此,我正在尝试考虑您的设置可能有什么问题。 Adfs 集成身份验证的工作方式与任何其他网站的集成身份验证工作方式完全相同。因此,一个虚拟站点是测试服务器是否有问题而不是 adfs。
-
@Wiktor Zychla 你好。我已经测试了虚拟应用程序,它似乎工作!我通过 Visual Studio 2012 创建了 ASP.NET MVC 4。并选择了“Intranet Application”。然后我发布了该项目并将我的 adfs testapp 放入应用程序中的同一服务器。将应用程序身份验证启用为“Windows 身份验证”并禁用“匿名身份验证”。运行应用程序并在右上角看到我的 Windows 用户名,但没有提供凭据。接下来是什么?我还需要做更多的测试吗?谢谢!!!
-
不要在论坛中暴露您的私人数据,例如证书指纹,这可能会导致安全漏洞:)
标签: c# asp.net single-sign-on windows-authentication adfs