ASP.Net Core 2.1 Vue.JS SPA - Cookie Auth 和 Vue.JS 开发服务器

ASP.Net Core 2.1 Vue.JS SPA - Cookie Auth and the Vue.JS Dev Server

我有一个应用程序,它本质上是一个 Vue.JS SPA,位于 dotnet 核心 2.1 应用程序中,提供 API 服务。当我启动应用程序时,使用我当前的 Startup.cs 配置,它会启动 3 windows。其中 1 个 windows 是实际的 ASP.Net 核心应用程序根目录 - 其他 2 个(不要问我为什么 2)是 Vue.js 开发服务器的副本,当你做 npm run serve.

我遇到的问题是,如果我在第一个 window 中使用实例 运行,身份验证工作正常,但如果我尝试使用 Vue.JS 登录服务器 windows 然后我得到一个 401 返回。

我的第一个想法是 CORS,所以我设置了一个 CORS 策略,纯粹是为了 运行 在开发模式下,但这并没有解决这个问题。没有 Vue.JS 服务器的开发是不可行的,因为没有它我没有 HMR,每次我进行设计更改时都需要重新加载整个应用程序状态。

我之前在 .net Framework 4.6 API 后端使用过此设置,完全没有问题,所以我只能认为这是一种现代安全增强功能,它阻止了我需要配置的代理在开发或其他事情上离开。

欢迎就我如何解决这个问题提出任何建议。

这是我当前的 Startup.cs 配置...

       public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
        ApplicationDbContext context, RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
    {
        if (env.IsDevelopment())
        {
            app
                .UseDeveloperExceptionPage()
                .UseDatabaseErrorPage()
                .UseCors("DevelopmentPolicy");
        }
        else
        {
            app
                .UseExceptionHandler("/Home/Error")
                .UseHsts();
        }

        app
            .UseAuthentication()
            .UseHttpsRedirection()
            .UseStaticFiles()
            .UseSpaStaticFiles();

        app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

        app.UseSpa(spa =>
        {
            spa.Options.SourcePath = "VueApp";

            if (env.IsDevelopment())
            {
                spa.UseVueCliServer("serve");
            }
        });

        DbInitializer.Initialize(context, roleManager, userManager, env, loggerFactory);
    }

...和配置服务...

   public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddLogging(builder => builder
                .AddConsole()
                .AddDebug());

        services
            .AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());

        services
            .AddCors(options =>
            {
                options.AddPolicy("DevelopmentPolicy",
                    policy => policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
            });

        // In production, the Vue files will be served from this directory
        services.AddSpaStaticFiles(configuration => { configuration.RootPath = "wwwroot"; });
        services.AddAuthentication();

        services
            .ConfigureEntityFramework(Configuration)
            .ConfigureEntityServices()
            .ConfigureIdentityDependencies()
            .ConfigureDomainServices()
            .ConfigureApplicationCookie(config =>
            {
                config.SlidingExpiration = true;
                config.Events = new CookieAuthenticationEvents
                {
                    OnRedirectToLogin = cxt =>
                    {
                        cxt.Response.StatusCode = 401;
                        return Task.CompletedTask;
                    },
                    OnRedirectToAccessDenied = cxt =>
                    {
                        cxt.Response.StatusCode = 403;
                        return Task.CompletedTask;
                    },
                    OnRedirectToLogout = cxt => Task.CompletedTask
                };
            });
    }

...并且身份在此处与 EF 配置混为一谈...

       public static IServiceCollection ConfigureEntityFramework(this IServiceCollection services, string connectionString)
    {
        services
            .AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString))
            .AddIdentity<ApplicationUser, IdentityRole>(options =>
                {

                    // Password settings
                    options.Password.RequireDigit = true;
                    options.Password.RequiredLength = 8;
                    options.Password.RequireNonAlphanumeric = true;

                    // Lockout settings
                    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
                    options.Lockout.MaxFailedAccessAttempts = 10;

                    // User settings
                    options.User.RequireUniqueEmail = true;
                })
            .AddRoleManager<RoleManager<IdentityRole>>()
            .AddSignInManager<SignInManager<ApplicationUser>>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        return services;
    }

我的 vue.config.js 看起来像这样...

const baseUrl = ''

module.exports = {
    publicPath: baseUrl + '/',

    // place our built files into the parent project where they will be copied
    // into the distribution for deployment
    outputDir: '../wwwroot',

    filenameHashing: true, //process.env.NODE_ENV === 'production',
    lintOnSave: 'error',

    css: {
        modules: false,
        sourceMap: process.env.NODE_ENV !== 'production',
        loaderOptions: {
            sass: {
                data: `
                    $fa-font-path: ${process.env.NODE_ENV !== 'production' ? '"~/fonts"' : '"' + baseUrl + '/fonts"'};
                    @import "@/scss/base/index.scss";
                    @import "@/scss/helpers/index.scss";
                `
            }
        }
    },

    devServer: {
        host: 'localhost',
        port: 8080,
        hot: true,
        open: true,
        openPage: '',
        overlay: true,
        disableHostCheck: true,
        proxy: {
            // Proxy services to a backend API
            '/api': {
                target: process.env.PROXY || 'https://localhost:44368',
                secure: false,
                changeOrigin: true
            }
        }
    },

    // these node_modules use es6 so need transpiling for IE
    transpileDependencies: [
    ]
}

我最终解决了这个问题,方法是配置应用程序,使其仅在不处于开发模式时强制使用 HTTPS。 HTTPS 正在杀死底层的 Webpack 开发服务器,因此热模块替换 - 我现在可以在主 window!

中成功调试

很高兴投票给比这更好的解决方案,尽管它可以在不终止 SSL 的情况下工作(如果可能的话)。