想实现这样的登录验证,当当前请求是通过 SPA 的 index.html 静态响应的,在服务端验证当前用户是否已登录,未登录跳转到登录页,请问如何实现?
通过下面的代码实现,准备庆祝成功的时候,被一个问题泼了一盆冷水
public void Configure(IApplicationBuilder app)
{
app.UseSpa(spa =>
{
spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions
{
OnPrepareResponse = PrepareResponse
};
});
}
private static void PrepareResponse(StaticFileResponseContext fileContext)
{
if (fileContext == null) return;
var headers = fileContext.Context.Response.Headers;
headers.Remove("cache-control");
headers.Add("cache-control", "no-store, no-cache, must-revalidate");
var httpContext = fileContext.Context;
if (!httpContext.User.Identity.IsAuthenticated)
{
httpContext.Response.ContentLength = 0;
httpContext.Response.Body = Stream.Null;
var signInUrl = SignInUrl + WebUtility.UrlEncode(httpContext.Request.GetDisplayUrl());
httpContext.Response.Redirect(signInUrl);
}
}
UseSpa
所使用的 SpaDefaultPageMiddleware
修改了 Request.Path
,造成了跳转到登录页时无法提供原始的请求地址,以下是 SpaDefaultPageMiddleware 对应的实现代码
// Rewrite all requests to the default page
app.Use((context, next) =>
{
// If we have an Endpoint, then this is a deferred match - just noop.
if (context.GetEndpoint() != null)
{
return next(context);
}
context.Request.Path = options.DefaultPage;
return next(context);
});
通过引入一个 middleware 保存原始请求 url 解决了
public class AdminSpaMiddleware
{
public const string OriginRequestUrl = nameof(OriginRequestUrl);
private readonly RequestDelegate _next;
public AdminSpaMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext httpContext)
{
if (httpContext.GetEndpoint() is null)
{
httpContext.Items[OriginRequestUrl] = httpContext.Request.GetDisplayUrl();
}
return _next(httpContext);
}
}
在 app.UseSpa()
之前添加这个中间件
app.UseAdminSpaMiddleware();
PrepareResponse 方法中通 HttpContext.Items 获取原始请求 url
private static void PrepareResponse(StaticFileResponseContext fileContext)
{
var httpContext = fileContext.Context;
if (!httpContext.User.Identity.IsAuthenticated)
{
httpContext.Response.ContentLength = 0;
httpContext.Response.Body = Stream.Null;
var returnUrl = WebUtility.UrlEncode(httpContext.Items[AdminSpaMiddleware.OriginRequestUrl] as string ?? string.Empty);
var signInUrl = SignInUrl + returnUrl;
httpContext.Response.Redirect(signInUrl);
}
}