我有一个旧版 ASP.NET 4.8 MVC 应用程序,正在将其迁移为使用 Microsoft.Owin 框架的 Azure AD 身份验证。应用程序配置在我的本地计算机上运行良好。但是,我在生产服务器上运行它时遇到了问题。
生产环境代理设置:应用程序托管在代理后面的 IIS 服务器上。 SSL 在代理处终止,HTTP 请求转发到 IIS 服务器。
当我在生产服务器上浏览受保护的页面时,我被正确重定向到login.microsoftonline.com登录页面并且可以成功登录。但是,登录后,生产服务器上未设置 .AspNet.Cookies 身份验证 cookie,导致应用程序和 Azure AD /authorize 端点之间出现重定向循环。
我假设发出“.AspNet.Cookies”cookie 时出现问题,因为我在浏览器中没有看到该问题。如何证实这一点?或被追踪?
这是在startup.cs类中配置的方式。我遵循了一些在线文章来实现这一点并且在我的本地运行良好。
public void Configuration(IAppBuilder app)
{
// Configure Auth0 parameters
string auth0Domain = ConfigurationManager.AppSettings["auth0:Domain"];
string auth0ClientId = ConfigurationManager.AppSettings["auth0:ClientId"];
string auth0RedirectUri = ConfigurationManager.AppSettings["auth0:RedirectUri"];
string auth0PostLogoutRedirectUri = ConfigurationManager.AppSettings["auth0:PostLogoutRedirectUri"];
// Set Cookies as default authentication type
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
LoginPath = new PathString("/Account/Login"),
CookieSameSite = Microsoft.Owin.SameSiteMode.None,
// More information on why the CookieManager needs to be set can be found here:
// https://github.com/aspnet/AspNetKatana/wiki/System.Web-response-cookie-integration-issues
CookieManager = new SameSiteCookieManager(new SystemWebCookieManager())
});
// Configure Auth0 authentication
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "Auth0",
Authority = auth0Domain,
ClientId = auth0ClientId,
RedirectUri = auth0RedirectUri,
PostLogoutRedirectUri = auth0PostLogoutRedirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = "openid profile email",
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
// More information on why the CookieManager needs to be set can be found here:
// https://docs.microsoft.com/en-us/aspnet/samesite/owin-samesite
CookieManager = new SameSiteCookieManager(new SystemWebCookieManager()),
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var client = new HttpClient();
var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
{
Code = n.Code,
Address = "https://login.microsoftonline.com/2d55c52b-2865-45c0-b174-fef7eed8e331/oauth2/v2.0/token",
ClientId = auth0ClientId,
ClientSecret = "WLT8Q~KKe2S5k~XrzUsz-XJUiRPdgaiBAx1uYcnr",
RedirectUri = auth0RedirectUri,
});
if (tokenResponse.IsError)
throw new Exception(tokenResponse.Error);
var response = await client.GetUserInfoAsync(new UserInfoRequest
{
Token = tokenResponse.AccessToken,
Address = "https://graph.microsoft.com/oidc/userinfo",
});
if (response.IsError)
throw new Exception(response.Error);
n.AuthenticationTicket.Identity.AddClaims(response.Claims);
},
RedirectToIdentityProvider = notification =>
{
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
{
}
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var logoutUri = $"https://{auth0Domain}/v2/logout?client_id={auth0ClientId}";
var postLogoutUri = notification.ProtocolMessage.PostLogoutRedirectUri;
if (!string.IsNullOrEmpty(postLogoutUri))
{
if (postLogoutUri.StartsWith("/"))
{
// transform to absolute
var request = notification.Request;
postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
}
logoutUri += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}";
}
notification.Response.Redirect(logoutUri);
notification.HandleResponse();
}
return Task.FromResult(0);
}
}
});
}
如何确保 .AspNet.Cookies cookie 在生产服务器上正确设置?
是否需要额外的配置来处理代理处的 SSL 终止?
编辑:添加了 web.config 文件
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
https://go.microsoft.com/fwlink/?LinkId=301880
-->
<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=4.4.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="sdx.logging" type="SDX.Logging.SDXLoggingConfiguration, SDX.System" />
<section name="sdx.services" type="SDX.Services.SDXServicesConfiguration, SDX.Services" />
<section name="sdx.resources" type="SDX.Resources.SDXResourcesConfiguration, SDX.Resources" />
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="true" />
<add key="PreserveLoginUrl" value="true" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="auth0:Domain" value="https://login.microsoftonline.com/xxxx/v2.0" />
<add key="auth0:ClientId" value="f14563a5c4" />
<add key="auth0:RedirectUri" value="http://localhost:49141" />
<add key="auth0:PostLogoutRedirectUri" value="http://localhost:49141" />
</appSettings>
<connectionStrings>
</connectionStrings>
<log4net debug="true">
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="logs\log.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<datePattern value="yyyyMMdd" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] – %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>
<!--
maxLogAgeDays = How many days of logs to keep. Max=7.
traceLevel =
Error: Output error-handling messages.
Warning: Output warnings and error-handling messages.
Info: Output informational messages, warnings, and error-handling messages.
Verbose: Output all debugging and tracing messages.
-->
<sdx.logging traceLevel="Verbose" cleanupInterval="1.00:00:00" logFilename="C:\SourceCodes\VSTS\Logs\%d\web.log">
<cleanupDirectories>
<add name="C:\SourceCodes\VSTS\Logs\" />
</cleanupDirectories>
</sdx.logging>
<system.web>
<compilation debug="true" targetFramework="4.8" />
<httpRuntime targetFramework="4.8" />
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" />
<bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Web.Infrastructure" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-2.0.1.0" newVersion="2.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.2.9.0" newVersion="5.2.9.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Logging" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Tokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.JsonWebTokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Protocols" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Protocols.OpenIdConnect" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" />
</compilers>
</system.codedom>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="ServiceEndpointBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="None" />
</serviceCertificate>
</clientCredentials>
</behavior>
<behavior name="MyServiceBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="10" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="None">
<message clientCredentialType="None" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint name="NetTcpBinding_IMySampleAppService" address="net.tcp://localhost:8585/MySampleAppService" binding="netTcpBinding" bindingConfiguration="NetTcpBinding" contract="SDX.GCPS.Models.IMySampleAppService" behaviorConfiguration="ServiceEndpointBehavior">
<identity>
<servicePrincipalName value="hostname/localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
在 Web.config 中配置代理设置:
<configuration>
<system.net>
<defaultProxy>
<proxy autoDetect="true" />
</defaultProxy>
</system.net>
</configuration>
用于 HTTP 标头转发的自定义中间件:
using System.Threading.Tasks;
using System.Web;
public class ForwardHeadersMiddleware : OwinMiddleware
{
public ForwardHeadersMiddleware(OwinMiddleware next) : base(next) { }
public override async Task Invoke(IOwinContext context)
{
var request = context.Request;
var response = context.Response;
// Forward the headers
var xForwardedFor = request.Headers["X-Forwarded-For"];
var xForwardedProto = request.Headers["X-Forwarded-Proto"];
var xForwardedHost = request.Headers["X-Forwarded-Host"];
if (!string.IsNullOrEmpty(xForwardedFor))
{
response.Headers.Set("X-Forwarded-For", xForwardedFor);
}
if (!string.IsNullOrEmpty(xForwardedProto))
{
response.Headers.Set("X-Forwarded-Proto", xForwardedProto);
}
if (!string.IsNullOrEmpty(xForwardedHost))
{
response.Headers.Set("X-Forwarded-Host", xForwardedHost);
}
await Next.Invoke(context);
}
}