ASP.NET Core Razor组件HttpContext访问异常问题求助
ASP.NET Core Razor组件中HttpContext访问不一致问题
问题描述
我在ASP.NET Core的Razor组件页面中遇到HttpContext访问不一致的问题。需要从Razor组件页面访问HttpContext,当前在OnInitialized()事件中读取Cookie和Session字符串,但这个事件会被调用两次:第一次能正常读取Cookie与Session;第二次HttpContextAccessor.HttpContext返回null,无法访问Cookie和Session变量。该问题仅出现在预发布环境,开发环境运行正常。
我的_Host.cshtml使用了render-mode="ServerPrerendered",这导致OnInitialized()被两次调用;若改为render-mode="Server",则无法读取Cookie。我们采用智能卡进行用户认证。
Razor页面代码(/certcheck5)
@page "/certcheck5" <PageTitle>Certificate Check</PageTitle> @using System.Diagnostics; @using Microsoft.Extensions.Options; @using Microsoft.AspNetCore.Http; @using Microsoft.Extensions.Logging; @using System.Security.Cryptography.X509Certificates; @using CIS_2.Helpers; @inject ILogger<SearchParameters> Logger; @inject IHttpContextAccessor HttpContextAccessor; <h1>Certificate Check</h1> <p> Retrieves HttpContext.Connection.ClientCertificate.<br> Enumerates Session Variable.<br><br> <label> <input type="checkbox" @bind="shouldRender" /> ShouldRender? </label> </p> <p> <div>Counter: @iCnt.ToString()</div> <button @onclick="IncrementCount">Increment Counter</button> </p> <p> <h4>HttpContext</h4> <div>USER CERT Subject: @Subject</div> <div>USER CERT Thumbprint: @Thumbprint</div> <div>USER CERT Expiry Date: @Expires</div> <div>REQUEST COOKIE[AuthCookie]: @AuthCookie</div> </p> <p> <h4>Session Parameters</h4> <div>Status: @Status</div> <div>SessionVar: @SessionVar</div> </p> @code { private bool shouldRender = true; private bool bLoading = true; private X509Certificate2 UserCert; private string Subject = string.Empty; private string Thumbprint = string.Empty; private string Expires = string.Empty; private string AuthCookie = string.Empty; private string SessionVar = string.Empty; private string Status = string.Empty; private int iCnt = 0; protected override void OnInitialized() { Logger.LogWarning("***CertCheck5.OnInitialized() invoke"); string auth_Cookie = string.Empty; try { auth_Cookie = HttpContextAccessor.HttpContext.Request.Cookies["FMLO-auth"]; Status = "Starting"; Status = HttpContextAccessor.HttpContext.ToString(); Status = HttpContextAccessor.HttpContext.Session.ToString(); SessionVar = HttpContextAccessor.HttpContext.Session.GetString("SearchParameters"); Debug.WriteLine("***Session.Restore() worked"); Logger.LogWarning("***Session.Restore() worked"); } catch (Exception exc) { Logger.LogWarning("***Session.Restore() failed:" + exc.Message); } if (HttpContextAccessor.HttpContext is not null) { X509Certificate2 UserCert = HttpContextAccessor.HttpContext.Connection.ClientCertificate; try { // This is called more than once the cookie is null the first time. auth_Cookie = HttpContextAccessor.HttpContext.Request.Cookies["FM-auth"]; } catch (Exception exc) { Logger.LogWarning("***HttpContextAccessor.HttpContext.Request.Cookies failed:" + exc.Message); } if (auth_Cookie is not null) { if (Subject.Length == 0) Subject = UserCert.Subject.ToString(); if (Thumbprint.Length == 0) Thumbprint = UserCert.Thumbprint; if (Expires.Length == 0) Expires = UserCert.NotAfter.ToString(); if (AuthCookie.Length == 0) AuthCookie = auth_Cookie; } } } protected override bool ShouldRender() { Debug.WriteLine("***ShouldRender"); Logger.LogWarning("***ShouldRender"); return shouldRender; //return base.ShouldRender(); } private void IncrementCount() { iCnt++; } }
Program.cs代码
using Microsoft.AspNetCore.Authentication.Certificate; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using System.IO; using System.Diagnostics; using Microsoft.AspNetCore.StaticFiles.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using System; WebApplicationBuilder builder; builder = WebApplication.CreateBuilder( new WebApplicationOptions { ContentRootPath = Directory.GetCurrentDirectory(), WebRootPath = Directory.GetCurrentDirectory() + "\\wwwroot" } ); // Add services to the container. builder.Services.AddAuthentication( CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(options => { options.AllowedCertificateTypes = CertificateTypes.All; options.Events = new CertificateAuthenticationEvents { OnCertificateValidated = context => { //Leveraging the DP injected single instance of the CertificateValidation Service var validationService = context.HttpContext.RequestServices.GetService<ICertValidation>(); // Here it validate and call the methode ValidateCertificate from CertificateValidationService class. if (validationService.ValidateCertificate(context.ClientCertificate)) { context.Success(); } else { context.Fail("Invalid certificate"); } return Task.CompletedTask; }, OnAuthenticationFailed = context => { context.Fail("Invalid certificate"); return Task.CompletedTask; } }; }); builder.Services.AddAuthorization(options => { // By default, all incoming requests will be authorized according to the default policy options.FallbackPolicy = options.DefaultPolicy; }); builder.Services.AddRazorPages(); builder.Services.AddControllers(); builder.Services.AddServerSideBlazor(); builder.Services.AddSession(options => { options.Cookie.IsEssential = true; }); builder.Services.AddDistributedMemoryCache(); builder.Services.AddMvc(); builder.Services.AddSingleton<ICertValidation, CertificateValidationService>(); builder.Services.AddHttpContextAccessor(); builder.Services.AddCertificateForwarding(options => { options.CertificateHeader = "X-SSL-CERT"; options.HeaderConverter = headerValue => { X509Certificate2? clientCertificate = null; if (!string.IsNullOrWhiteSpace(headerValue)) { clientCertificate = new X509Certificate2(StringToByteArray(headerValue)); } return clientCertificate!; static byte[] StringToByteArray(string hex) { var numberChars = hex.Length; var bytes = new byte[numberChars / 2]; for (int i = 0; i < numberChars; i += 2) { bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); } return bytes; } }; }); builder.Services.Configure<CisSettings>(builder.Configuration.GetSection("Settings")); var app = builder.Build(); app.UseAuthentication(); //app.UseAuthorization(); var sPDF_Physicialroot = builder.Configuration["Settings:PDF_Physicialroot"]; var sPDF_InvoiceBaseURL = builder.Configuration["Settings:PDF_InvoiceBaseURL"]; // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseStaticFiles(); // this adds a folder that will render static files from "/pdf_invoices (PDF_InvoiceBaseURL) // wwwroot\cis\PDF_Invoices SharedOptions sharedOptions = new SharedOptions(); sharedOptions.FileProvider = new PhysicalFileProvider(sPDF_Physicialroot); sharedOptions.RequestPath = new PathString(sPDF_BaseURL); StaticFileOptions staticFileOptions = new StaticFileOptions(sharedOptions); app.UseStaticFiles(staticFileOptions); app.Logger.LogWarning("Trace Message from Program.cs"); app.UseSession(); //begin SetString() hack app.Use(async delegate (HttpContext Context, Func<Task> Next) { //this throwaway session variable will "prime" the SetString() method //to allow it to be called after the response has started var TempKey = Guid.NewGuid().ToString(); //create a random key Context.Session.Set(TempKey, Array.Empty<byte>()); //set the throwaway session variable Context.Session.Remove(TempKey); //remove the throwaway session variable string auth_Cookie = $"None"; Context.Request.Cookies.TryGetValue("FM-auth", out auth_Cookie); string msg = "Program.cs auth_Cookie=" + auth_Cookie; app.Logger.LogWarning(msg); SessionParameters sessionParameters; ISearchParameters searchParameters; searchParameters = new SearchParameters(app.Logger); sessionParameters = new SessionParameters(app.Logger); if (!sessionParameters.Exists(Context.Session)) { searchParameters.OnInitialized(Uics, auth_Cookie); sessionParameters.StoreMain(Context.Session, searchParameters); } await Next(); //continue on with the request }); //end SetString() hack Debug.WriteLine($"Content Root Path: {builder.Environment.ContentRootPath}"); Debug.WriteLine($"WebRootPath: {builder.Environment.WebRootPath}"); Debug.WriteLine($"PDFRootPath: {sPDF_Physicialroot}"); app.UseRouting(); app.UseEndpoints(endpoints => { app.MapControllers(); app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); } ); app.Run();
内容的提问来源于stack exchange,提问作者Remie Smith
相关产品推荐
相关产品推荐

