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 的情况下工作(如果可能的话)。
我有一个应用程序,它本质上是一个 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 的情况下工作(如果可能的话)。