防止在asp.net core 2.2中重定向到/Account/Login

问题描述 投票:0回答:5

我试图阻止应用程序在用户未登录时重定向到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.BaseDirectory}Contest.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",
        });
    }

}

我的设置有什么问题?

asp.net-core-2.2
5个回答
45
投票

您遇到的行为与您使用 ASP.NET Identity 相关。 当您调用

services.AddIdentity
时,会在后台注册基于 cookie 的身份验证方案并将其设置为默认质询方案,如您在 GitHub 上此处的代码中看到的那样。

即使您自己注册了 cookie 身份验证方案并将其设置为默认方案,特定的默认方案(如

AuthenticateScheme

ChallengeScheme
SignInScheme
 等)仍具有优先权。 
DefaultScheme
仅在未设置特定的情况下由认证系统使用。

要回答您的问题,您可以使用辅助方法

services.ConfigureApplicationCookie

将配置设置应用于 ASP.NET Identity 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 本身处理的。


12
投票
如果您

使用ASP.NETIdentity,您可以遵循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; }; }); }
仅将路径设置为空/空是不够的。


5
投票
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; }; });
    

0
投票
我遇到了这个问题并采取了解决方法,我只需创建一个控制器“帐户”并在其中写入重定向:

public class AccountController : Controller { public IActionResult Login() { return RedirectToAction("Login", "Home"); } }
    

-1
投票
有点晚了,但也许其他人也会有和我一样的事情。

那么我有什么代码?

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; } };
当我把它移到顶部后,它就开始工作了!奇怪但有效!

© www.soinside.com 2019 - 2024. All rights reserved.