.NET 5 - Cookie 验证挑战 rewrite/re-execute 而不是重定向
.NET 5 - Cookie authetication on challenge rewrite/re-execute instead of redirect
我们目前正在将 .NET Framework 4.8 MVC 应用程序迁移到 .NET (Core) 5,我遇到了一个挑战。
在旧框架应用程序中,我们实现了 AuthorizeAttribute
,其中对 HandleUnauthorizedRequest
的覆盖将清除响应的内容并 're'- 执行另一个控制器(借助 IControllerFactory
).这实质上创建了一个登录登录页面,其中 URL 保持不变(相同的请求)。对于具有此属性的每个安全页面,我们都会呈现一个不同的页面(如您所愿,'sales pitch')。
现在,在 .NET Core 中,身份验证的工作方式与在 .NET Framework 中不同。所以我相信前面描述的方法在这里行不通。我认为正确的方法是配置 cookie 身份验证来处理这个问题。但是我们有两个业务需求使这有点挑战:
- 没有重定向;相反,在第一次请求时显示登录页面,所以基本上,URL rewrite/re-execute 而不是重定向
- 每个需要身份验证的页面都应呈现不同的登录页面
我的挑战在于第一个要求。我需要找到一种方法来在身份验证时呈现不同的控制器操作。 URL 重新执行也可以,因为我们已经在自定义 UseStatusCodePages 处理程序中这样做了。 AddAuthentication().AddCookie(o => o.Events.OnRedirectToLogin = context => { ... }
看起来像是我可以处理我的逻辑的地方,但我尝试过的任何东西都不起作用。原因可能是中间件太远了?
所以问题是:如何重写或重新执行身份验证质询而不是重定向?
在同事的帮助下,通过查看 UseStatusCodePages
和 UseRewriter
功能的源代码,我们找到了解决问题的办法。
我们最终在 AddCookie
上实施了 OnRedirectToLogin
事件。当这个事件被触发时,我们找到我们需要显示的当前登录页面的 URI。但是我们没有设置 context.RedirectUri
,而是将其添加到 context.HttpContext.Items
字典中。然后我们使用自定义中间件来查找该项目并在那里执行 rewrite/re-execute 。 return Task.CompletedTask;
确保没有任何反应,没有重定向。没有中间件,我们得到一个空白页面。
services.AddAuthentication().AddCookie(o =>
{
o.Events.OnRedirectToLogin = context =>
{
var loginPageUri = GetLoginLandingPageUri(context);
context.HttpContext.Items[ReExecuteUriMiddleware.ReExecuteUriItemKey] = loginPageUri;
return Task.CompletedTask;
};
});
当中间件在 Items 字典中看到该项目时,我们会重置端点信息,因此当我们第二次调用 _next
时,中间件管道会很好地 re-execute 请求,但是有不同的路径。
public class ReExecuteUriMiddleware
{
public const string ReExecuteUriItemKey = "ReExecuteUri";
private readonly RequestDelegate _next;
public ReExecuteUriMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
await _next.Invoke(httpContext);
if (httpContext.Items[ReExecuteUriItemKey] is string reExecuteUri)
{
// An endpoint may have already been set. Since we're going to re-invoke the middleware pipeline we need to reset
// the endpoint and route values to ensure things are re-calculated.
httpContext.SetEndpoint(null);
var routeValuesFeature = httpContext.Features.Get<IRouteValuesFeature>();
routeValuesFeature?.RouteValues?.Clear();
httpContext.Request.Path = reExecuteUri;
await _next.Invoke(httpContext);
}
}
}
当然我们在 Startup
class app.UseStatusCodePages()
之后添加了它。
app.UseMiddleware<ReExecuteUriMiddleware>();
我们目前正在将 .NET Framework 4.8 MVC 应用程序迁移到 .NET (Core) 5,我遇到了一个挑战。
在旧框架应用程序中,我们实现了 AuthorizeAttribute
,其中对 HandleUnauthorizedRequest
的覆盖将清除响应的内容并 're'- 执行另一个控制器(借助 IControllerFactory
).这实质上创建了一个登录登录页面,其中 URL 保持不变(相同的请求)。对于具有此属性的每个安全页面,我们都会呈现一个不同的页面(如您所愿,'sales pitch')。
现在,在 .NET Core 中,身份验证的工作方式与在 .NET Framework 中不同。所以我相信前面描述的方法在这里行不通。我认为正确的方法是配置 cookie 身份验证来处理这个问题。但是我们有两个业务需求使这有点挑战:
- 没有重定向;相反,在第一次请求时显示登录页面,所以基本上,URL rewrite/re-execute 而不是重定向
- 每个需要身份验证的页面都应呈现不同的登录页面
我的挑战在于第一个要求。我需要找到一种方法来在身份验证时呈现不同的控制器操作。 URL 重新执行也可以,因为我们已经在自定义 UseStatusCodePages 处理程序中这样做了。 AddAuthentication().AddCookie(o => o.Events.OnRedirectToLogin = context => { ... }
看起来像是我可以处理我的逻辑的地方,但我尝试过的任何东西都不起作用。原因可能是中间件太远了?
所以问题是:如何重写或重新执行身份验证质询而不是重定向?
在同事的帮助下,通过查看 UseStatusCodePages
和 UseRewriter
功能的源代码,我们找到了解决问题的办法。
我们最终在 AddCookie
上实施了 OnRedirectToLogin
事件。当这个事件被触发时,我们找到我们需要显示的当前登录页面的 URI。但是我们没有设置 context.RedirectUri
,而是将其添加到 context.HttpContext.Items
字典中。然后我们使用自定义中间件来查找该项目并在那里执行 rewrite/re-execute 。 return Task.CompletedTask;
确保没有任何反应,没有重定向。没有中间件,我们得到一个空白页面。
services.AddAuthentication().AddCookie(o =>
{
o.Events.OnRedirectToLogin = context =>
{
var loginPageUri = GetLoginLandingPageUri(context);
context.HttpContext.Items[ReExecuteUriMiddleware.ReExecuteUriItemKey] = loginPageUri;
return Task.CompletedTask;
};
});
当中间件在 Items 字典中看到该项目时,我们会重置端点信息,因此当我们第二次调用 _next
时,中间件管道会很好地 re-execute 请求,但是有不同的路径。
public class ReExecuteUriMiddleware
{
public const string ReExecuteUriItemKey = "ReExecuteUri";
private readonly RequestDelegate _next;
public ReExecuteUriMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
await _next.Invoke(httpContext);
if (httpContext.Items[ReExecuteUriItemKey] is string reExecuteUri)
{
// An endpoint may have already been set. Since we're going to re-invoke the middleware pipeline we need to reset
// the endpoint and route values to ensure things are re-calculated.
httpContext.SetEndpoint(null);
var routeValuesFeature = httpContext.Features.Get<IRouteValuesFeature>();
routeValuesFeature?.RouteValues?.Clear();
httpContext.Request.Path = reExecuteUri;
await _next.Invoke(httpContext);
}
}
}
当然我们在 Startup
class app.UseStatusCodePages()
之后添加了它。
app.UseMiddleware<ReExecuteUriMiddleware>();