@zakharoffam
Начинающий

Как настроить CORS для React для запросов к API другого домена?

Имеется следующие компоненты.
1. IIS без аутентификации клиентов, который отдает SPA-приложение.
2. SPA написаное на React с axios'ом на борту.
3. Еще 2 IIS с доменной авторизации через Kerberos, которые служат прокси-серверами до нескольких REST API, написаных на Node.

Проблема в следующем: как только заходим на первый IIS он отдает нам SPA. Оно загружается успешно, а дальше стучится к API, которые находятся за IIS-проксей и тут сразу же возникает ошибка. IIS требует авторизацию, а браузер не хочет отвечать на запрос и получается просто 401 ошибка.

На всех IIS'ах включены следующие заголовки:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true

В настройках axios в SPA тоже самое, но запроса на авторизацию так и не возникает.
Подскажите, чего им всем не хватает?

В Access-Control-Allow-Origin пробовал подсовывать и адреса IIS по одному и массив всех разом, толку нет.
  • Вопрос задан
  • 60 просмотров
Решения вопроса 1
firedragon
@firedragon
Senior .NET developer
Это не нода, просто проверьте заголовки на совпадение

using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SpaServices;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using TwoRecycle.Code;
using TwoRecycle.Data;
using TwoRecycle.Model;
using VueCliMiddleware;

namespace TwoRecycle
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseMySql(Configuration.GetConnectionString("DefaultConnection")));
            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddDefaultUI()
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services.AddIdentityServer()
                .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

            services.AddAuthentication()
                .AddIdentityServerJwt();
            services.Configure<JwtBearerOptions>(
                IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
                options =>
                {
                    var onTokenValidated = options.Events.OnTokenValidated;
                    options.Events.OnTokenValidated = async context => { await onTokenValidated(context); };
                });


            services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>,
                AdditionalUserClaimsPrincipalFactory>();

            services.Configure<IdentityOptions>(options =>
            {
                // Password settings.
                options.Password.RequireDigit = true;
                options.Password.RequireLowercase = true;
                options.Password.RequireNonAlphanumeric = true;
                options.Password.RequireUppercase = true;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 1;

                // Lockout settings.
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                options.Lockout.MaxFailedAccessAttempts = 5;
                options.Lockout.AllowedForNewUsers = true;

                // User settings.
                options.User.AllowedUserNameCharacters =
                    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
                options.User.RequireUniqueEmail = false;
            });

            services.ConfigureApplicationCookie(options =>
            {
                // Cookie settings
                options.Cookie.HttpOnly = true;
                options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
                options.LoginPath = "/Identity/Account/Login";
                options.AccessDeniedPath = "/Identity/Account/AccessDenied";
                options.SlidingExpiration = true;
            });
            services.AddControllersWithViews();
            services.AddRazorPages();
            // NOTE: PRODUCTION Ensure this is the same path that is specified in your webpack output
            services.AddSpaStaticFiles(opt => opt.RootPath = "wwwroot");
            services.AddAuthorization(config =>
            {
                config.AddPolicy("IsAdmin", policy => policy.RequireClaim("IsAdmin", "true"));
            });
            services.AddCors(options =>
            {
                // this defines a CORS policy called "default"
                options.AddPolicy("default", policy =>
                {
                    policy.WithOrigins("https://localhost:44343", "https://www.2recycle.ru")
                        .AllowAnyHeader()
                        .AllowAnyMethod();
                });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                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.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();
            app.UseCors("default");
            app.UseAuthentication();
            app.UseIdentityServer();
            app.UseAuthorization();


            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");

                endpoints.MapRazorPages();
                //todo: client from here https://github.com/joaojosefilho/vuejsOidcClient

                // NOTE: VueCliProxy is meant for developement and hot module reload
                // NOTE: SSR has not been tested
                // Production systems should only need the UseSpaStaticFiles() (above)
                // You could wrap this proxy in either
                // if (System.Diagnostics.Debugger.IsAttached)
                // or a preprocessor such as #if DEBUG
                endpoints.MapToVueCliProxy(
                    "{*path}",
                    new SpaOptions { SourcePath = "../vue-app" },
                    npmScript: (System.Diagnostics.Debugger.IsAttached) ? "serve" : null,
                    regex: "Compiled successfully",
                    forceKill: true
                );

            });
        }
    }
}


Request URL: https://localhost:44343/Identity/Account/Login
Request Method: POST
Status Code: 302 
Remote Address: [::1]:44343
Referrer Policy: strict-origin-when-cross-origin
access-control-allow-origin: https://localhost:44343
cache-control: no-cache
date: Fri, 20 Nov 2020 18:08:42 GMT
expires: Thu, 01 Jan 1970 00:00:00 GMT
location: /
pragma: no-cache
server: Microsoft-IIS/10.0
set-cookie: idsrv.session=awkFp-5bzZoUGr5YmW0ILw; path=/; secure; samesite=none
set-cookie: .AspNetCore.Identity.Application=CfDJ8HSLnn4dT-FDg-hkyrBA0cj1cM6qHlOijWSDj9T492YPP264RtbSYW_KCJu603bg-uXJ6sOTX2apHtiQSSF4zgjcsHmLkOWOQYHiaOBfHCJhefscm4tvrtp8Y1fvzBzdj_NFv8MGvhGPDRkwPaQioPCMoxEAescm_wVdsd9q-OLMu-2SFCoauRGyp4m-94ljj0hqWemXD5SRw3U-XH5-ZihjLYRJxb0VI-Ih2CQAGsslCk0M7CfP9Dfvlnj_xtIXzyi2cdfFOxxCRzJ_HAfV9t9cFVPKdkPj58ZEQwDPysV66UPPAk04rPoBxihwTKshdhGcrGXvennVqcSwf4kyIWtNUMgXes0eaFmRueWuUbNNfhgCVfAC5fe6FE4MEvFoqVpjUz3K0rGNV2qMcLlAtfRPNhF2QOEBGD5NnHwgQwziNOo4fvv5mybNHuEsVDMt7xDX2agjk_Lr34kAYbJxfusKx0amxqAnhvIIaMlLruz5HMZOLNfReqUsqhclgJM2Nq2AD57rfurp41qJAGZftS37BAhSjci24WsD5X5m_TQ6qGh74CbyUY4gc6kMmqQbE6GrPyVCOdV_E7eCj8q7ZzUCyM3Z8k1xSx8Sn4KXR8J8DyVLM9HBNEMtMFaaIWBqI2oy8JhUaFbybeQa29u92GRiqFR7a81mS9bpxqDOvXC81IduLWXZyzyJOxuFhm75Ju19MorwmpFQOkLvlJUvr_rtUbXhwd0rXjjlGqAMULEP; path=/; secure; samesite=none; httponly
set-cookie: .AspNetCore.Mvc.CookieTempDataProvider=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=lax; httponly
vary: Origin
x-powered-by: ASP.NET
:authority: localhost:44343
:method: POST
:path: /Identity/Account/Login
:scheme: https
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
accept-encoding: gzip, deflate, br
accept-language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
cache-control: max-age=0
content-length: 266
content-type: application/x-www-form-urlencoded
cookie: .AspNetCore.Antiforgery.iEjq0RT987g=CfDJ8HSLnn4dT-FDg-hkyrBA0cjZI81abO-Klbwxcr-ZyIP1PHoFGyW39AWpPtmqiN0l706JrI9q7KPxvY97x4svXztoLzWo4qnySppUyB_GhDzeaOR3JQtYkRypJqWRAZfew8j2sqclVovCDkHAKhy5jlw
origin: https://localhost:44343
referer: https://localhost:44343/Identity/Account/Login
sec-fetch-dest: document
sec-fetch-mode: navigate
sec-fetch-site: same-origin
sec-fetch-user: ?1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36
Input.Email: koroten@ya.ru
Input.Password: 123
__RequestVerificationToken: CfDJ8HSLnn4dT-FDg-hkyrBA0cgTfwl1CtddD8AXkFr6sBqM25xP7qXeMJSe3TsUYICrsNUfzBhTEfuJ1wZjm3AbkVNAxbNUQ-57vnCKLCYLjgWKvjKO01fiXeMUVTjzwZIXwV4ktwvALnF2I2-pchmT6UU
Input.RememberMe: false
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы