防止在 asp.net core 2.2 中重定向到 /Account/Login
Posted
技术标签:
【中文标题】防止在 asp.net core 2.2 中重定向到 /Account/Login【英文标题】:Prevent redirect to /Account/Login in asp.net core 2.2 【发布时间】:2019-05-24 08:03:34 【问题描述】:当用户未登录时,我试图阻止应用重定向到 asp.net core 2.2 中的/Account/Login
。
即使我写了LoginPath = new PathString("/api/contests");
,任何未经授权的请求仍然被重定向到/Account/Login
这是我的 Startup.cs:
using System;
using System.Reflection;
using AutoMapper;
using Contest.Models;
using Contest.Tokens;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.Swagger;
namespace Contest
public class Startup
public IConfiguration Configuration get;
public Startup(IConfiguration configuration)
Configuration = configuration;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
services.AddAutoMapper();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
configuration.RootPath = "clientapp/build";
);
// ===== Add our DbContext ========
string connection = Configuration.GetConnectionString("DBLocalConnection");
string migrationAssemblyName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddDbContext<ContestContext>(options =>
options.UseSqlServer(connection,
sql => sql.MigrationsAssembly(migrationAssemblyName)));
// ===== Add Identity ========
services.AddIdentity<User, IdentityRole>(o =>
o.User.RequireUniqueEmail = true;
o.Tokens.EmailConfirmationTokenProvider = "EMAILCONF";
// I want to be able to resend an `Email` confirmation email
// o.SignIn.RequireConfirmedEmail = true;
).AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ContestContext>()
.AddTokenProvider<EmailConfirmationTokenProvider<User>>("EMAILCONF")
.AddDefaultTokenProviders();
services.Configure<DataProtectionTokenProviderOptions>(o =>
o.TokenLifespan = TimeSpan.FromHours(3)
);
services.Configure<EmailConfirmationTokenProviderOptions>(o =>
o.TokenLifespan = TimeSpan.FromDays(2)
);
// ===== Add Authentication ========
services.AddAuthentication(o => o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
options.Cookie.Name = "auth_cookie";
options.Cookie.SameSite = SameSiteMode.None;
options.LoginPath = new PathString("/api/contests");
options.AccessDeniedPath = new PathString("/api/contests");
options.Events = new CookieAuthenticationEvents
OnRedirectToLogin = context =>
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
,
;
);
// ===== Add Authorization ========
services.AddAuthorization(o =>
);
services.AddCors();
// ===== Add MVC ========
services.AddMvc(config =>
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
)
.AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore)
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// ===== Add Swagger ========
services.AddSwaggerGen(c =>
c.SwaggerDoc("v1", new Info
Title = "Core API",
Description = "Documentation",
);
var xmlPath = $"System.AppDomain.CurrentDomain.BaseDirectoryContest.xml";
c.IncludeXmlComments(xmlPath);
);
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseExceptionHandler("/Error");
app.UseHsts();
app.UseHttpsRedirection();
app.UseSpaStaticFiles(new StaticFileOptions()
);
app.UseCors(policy =>
policy.AllowAnyHeader();
policy.AllowAnyMethod();
policy.AllowAnyOrigin();
policy.AllowCredentials();
);
app.UseAuthentication();
app.UseMvc();
if (env.IsDevelopment())
app.UseSwagger();
app.UseSwaggerUI(c =>
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Core API");
);
app.UseSpa(spa =>
spa.Options.SourcePath = "clientapp";
if (env.IsDevelopment())
// spa.UseReactDevelopmentServer(npmScript: "start");
spa.UseProxyToSpaDevelopmentServer("http://localhost:3000");
);
我通过创建一个控制器来处理这个路由来绕过这个:
[Route("/")]
[ApiController]
public class UnauthorizedController : ControllerBase
public UnauthorizedController()
[HttpGet("/Account/Login")]
[AllowAnonymous]
public IActionResult Login()
HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
return new ObjectResult(new
StatusCode = StatusCodes.Status401Unauthorized,
Message = "Unauthorized",
);
我的设置有什么问题? 谢谢
【问题讨论】:
【参考方案1】:如果您不使用 ASP.NET Identity,则可以遵循 kuldeep chopra 在另一个答案中提到的相同模式,而是在 AddCookie
方法中:
public void ConfigureServices(IServiceCollection services)
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie((o) =>
o.Cookie.HttpOnly = true;
o.LoginPath = string.Empty;
o.AccessDeniedPath = string.Empty;
o.Events.OnRedirectToLogin = context =>
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
;
);
仅将路径设置为空/null 是不够的。
【讨论】:
【参考方案2】:这有点晚了,但也许其他人会和我有同样的事情。
那么我有什么代码?
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddScheme<CerveraProxyAuthenticationOptions, CerveraProxyAuthenticationSchemeHandler>(ProxyAuthentication.AuthenticationScheme, options => )
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
options.Events.OnRedirectToLogin = (o) =>
o.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return Task.CompletedTask;
;
options.Events.OnRedirectToAccessDenied = (o) =>
o.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return Task.CompletedTask;
;
if (cookieSettings.LocalizationCookieExpiration.TryConvertToTimeSpan(out TimeSpan expiration))
options.ExpireTimeSpan = expiration;
options.Cookie.MaxAge = expiration;
options.Cookie.SecurePolicy = cookieSettings.SecureCookies
? CookieSecurePolicy.Always
: CookieSecurePolicy.SameAsRequest;
options.Cookie.Path = "/";
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
SameSiteMode sameSiteMode = Enum.Parse<SameSiteMode>(cookieSettings.SameSite);
options.Cookie.SameSite = sameSiteMode;
options.Cookie.Name = cookieSettings.AuthenticationCookieName;
options.Events = new CookieAuthenticationEvents
OnSigningIn = async context =>
context.Options.Cookie.Domain = context.HttpContext.Request.Host.Host;
;
);
问题是我在范围底部有这段代码!
options.Events = new CookieAuthenticationEvents
OnSigningIn = async context =>
context.Options.Cookie.Domain = context.HttpContext.Request.Host.Host;
;
一旦我把它移到顶部,它就开始工作了!奇怪但有效!
【讨论】:
【参考方案3】:我遇到了这个问题并做了一个解决方法,我只是创建一个控制器“帐户”并在其中写入重定向:
public class AccountController : Controller
public IActionResult Login()
return RedirectToAction("Login", "Home");
【讨论】:
这只是针对实际问题的一个创可贴,另外它增加了一个不必要的重定向。【参考方案4】: services.ConfigureApplicationCookie(options =>
options.AccessDeniedPath = "/Account/Login";
options.LoginPath = "/Account/Denied";
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.Events.OnRedirectToLogin = context =>
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
;
);
【讨论】:
options.Events.OnRedirectToLogin = context => context.Response.StatusCode = StatusCodes.Status401Unauthorized;返回Task.CompletedTask; ;当 http 状态代码为 401 unathorized 时,此事件会阻止您将页面重定向到日志【参考方案5】:大家好,欢迎来到 *** ?
您遇到的行为与您使用 ASP.NET Identity 的事实有关。
当您调用services.AddIdentity
时,会在后台注册一个基于cookie 的身份验证方案并将其设置为默认质询方案,如代码here on GitHub 中所示。
即使您自己注册了一个 cookie 身份验证方案并将其设置为默认方案,但特定的默认方案(如 AuthenticateScheme
、ChallengeScheme
、SignInScheme
等)仍占优先地位。 DefaultScheme
仅在未设置特定的情况下由身份验证系统使用。
要回答您的问题,您可以使用辅助方法 services.ConfigureApplicationCookie
将配置设置应用于 ASP.NET 身份 cookie 选项,如下所示:
// ===== Add Identity ========
services.AddIdentity<User, IdentityRole>(o =>
o.User.RequireUniqueEmail = true;
o.Tokens.EmailConfirmationTokenProvider = "EMAILCONF";
// I want to be able to resend an `Email` confirmation email
// o.SignIn.RequireConfirmedEmail = true;
).AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ContestContext>()
.AddTokenProvider<EmailConfirmationTokenProvider<User>>("EMAILCONF")
.AddDefaultTokenProviders();
services.Configure<DataProtectionTokenProviderOptions>(o =>
o.TokenLifespan = TimeSpan.FromHours(3)
);
services.Configure<EmailConfirmationTokenProviderOptions>(o =>
o.TokenLifespan = TimeSpan.FromDays(2)
);
// ===== Configure Identity =======
service.ConfigureApplicationCookie(options =>
options.Cookie.Name = "auth_cookie";
options.Cookie.SameSite = SameSiteMode.None;
options.LoginPath = new PathString("/api/contests");
options.AccessDeniedPath = new PathString("/api/contests");
// Not creating a new object since ASP.NET Identity has created
// one already and hooked to the OnValidatePrincipal event.
// See https://github.com/aspnet/AspNetCore/blob/5a64688d8e192cacffda9440e8725c1ed41a30cf/src/Identity/src/Identity/IdentityServiceCollectionExtensions.cs#L56
options.Events.OnRedirectToLogin = context =>
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
;
);
这也意味着您可以安全地删除添加基于 cookie 的身份验证方案的部分,因为这由 ASP.NET Identity 本身负责。
告诉我你的进展如何!
【讨论】:
谢谢。像魅力一样工作! 如果您想确保所有内容都被重定向到 https 耦合,该怎么办?看,我发现我的网站重定向到 http,即使我的条目是 https 页面。以上是关于防止在 asp.net core 2.2 中重定向到 /Account/Login的主要内容,如果未能解决你的问题,请参考以下文章