#javascript #api #authentication #cookies #fetch
Вопрос:
Я пытаюсь получить файл cookie после успешного входа в систему, но не могу понять, как это сделать. В API идентификации ASP Net Core с использованием swagger браузер получает файл cookie, но когда я использую api выборки, я не могу получить файл cookie. Я попытался вернуть ответ return.json(); но это не работает. У меня также есть перенаправление на домашнюю страницу при успешном входе в систему, но я точно не знаю, как вернуть ответ.json(); если это необходимо.
Как API идентификации, так и JS — Cleint работают на локальном хосте.
JS — API выборки — СООБЩЕНИЕ:
function IdentityPost(formID, postUrl) {
const currForm = document.getElementById(formID); // Get the Form
var submitBtn = currForm.elements.namedItem("triggerSubmit"); // Get the submit button of the form
// Listen for Form- Submit
currForm.addEventListener('submit',function handler(e)
{
e.preventDefault(); // Prevent page reload on Submit
submitBtn.disabled = true; // Disable the submit button
LoadingMsg(); // Show Loading Message
// Get form data as string---------------------------------------------------------------
const formData = new FormData(this); // "this" = this Form
const searchParams = new URLSearchParams(formData); // Get the form data params
let formQueryString = searchParams.toString(); // Get the form data params as string
// POST ----------------------------------------------------------------------------------
fetch(identityApiUri postUrl formQueryString, // #1 = API-Address, #2 = API - Controller/Mehod, #3 = form data as sring
{
method: 'POST',
credentials: 'same-origin'
}).then(function (response)
{
// IF OK
if (response.status == 200 || response.status == 201) // Status 201 = "Created"
{
RemoveLoadingMsg();
SuccessMsg("Success");
currForm.reset(); // Reset the form
submitBtn.disabled = false; // Enable Submit button
if (document.referrer.split('/')[2] === window.location.host) // Return to previous page if local
{
history.back(); // Go back to previouse page
}
else
{
window.location.href = "/"; // RETURN TO Home
}
}
else // If Bad STATUS
{
return Promise.reject(response); // Triggers Catch method
}
}).catch(function (err) // If Exception
{
RemoveLoadingMsg();
// Show Error
try // Because of JSON Parse and err.text()
{
err.text().then(errorMessage => {
var error = errorMessage.substring(1, errorMessage.length - 1); // Remove the [..] form the Msg
ErrorMsg(error); // Get the error and display
});
}
catch(e)
{
console.warn("Post Exception - Probably No connection to hte server");
ErrorMsg(err " - Server is probably offline"); // Get the error and display
}
submitBtn.disabled = false; // Enable Submit button
console.warn('Post Exception:', err);
});
this.removeEventListener('submit', handler); // Remove Event Listener
});
}
ASP Net Core — Identity API — Запуск:
Я включил CORS Любого происхождения. — Не уверен, нужно ли мне включать это .allowCredentials(). Если я попытаюсь включить его, он скажет, что я не могу этого сделать .Функция AllowAnyOrigin() включена. Я получаю доступ к Api непосредственно из клиентского «Браузера».
using Leanheat.Identity.API.DBContexts;
using Leanheat.Identity.API.Filters;
using Leanheat.Identity.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
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 Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Leanheat.Identity.API
{
public class Startup
{
// Startup
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// Configure Services =================================================================================
public void ConfigureServices(IServiceCollection services)// This method gets called by the runtime. Use this method to add services to the container.
{
// Log in - DbContext
services.AddDbContextPool<LeanheatIdentityApiContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("IdentityContextConnection")));
// UnitOfWork - Filter
services.AddScoped<UnitOfWorkFilter>();
services.AddControllers(config => { config.Filters.AddService<UnitOfWorkFilter>(); }); // UnitOfWork for all Controllers
// CORS - Allow calling the API from WebBrowsers
services.AddCors();
// Log In
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
// Password settings
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
}).AddEntityFrameworkStores<LeanheatIdentityApiContext>().AddDefaultTokenProviders(); // AddDefaultTokenProviders is used for the Update Log In Password etc.
// Log In
// Make all Controllers protected by default so only Authorized Users can accsess them, for Anonymouse Users use [AlloAnonymouse] over the controllers.
services.AddMvc(options => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddXmlSerializerFormatters();
//services.AddControllers();
// Swagger
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Leanheat.Identity.API", Version = "v1" });
});
}
// Configure ===========================================================================================
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
{
// Default Code------------------------------------------------------------------------------------>
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Swagger
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Leanheat.Identity.API v1"));
}
app.UseHsts(); // Allow HTTPS
app.UseHttpsRedirection();
app.UseRouting();
// CORS - Allow calling the API from WebBrowsers
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.AllowAnyOrigin()
.SetIsOriginAllowed(origin => true));// allow any origin
// Log In
app.UseAuthentication(); // UseAuthentication SHOULD ALWAYS BE BEFORE Authorization
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Метод входа в систему в API идентификации:
// Log In ===================================================================================
[HttpPost]
[Route("LogIn")]
[AllowAnonymous]
public async Task<IActionResult> LogIn(string email, string password, bool rememberMe)
{
if(email != null amp;amp; password !=null)
{
var result = await signInManager.PasswordSignInAsync(email, password, rememberMe, false);
if (result.Succeeded) // If Login Ok
{
return new JsonResult(result);
}
return StatusCode(401, "[n "Invalid Log In" n]"); // If Erors return errors
}
return StatusCode(401, "[n "Email or Password cant be empty" n]");
}
Используя swagger, я могу получить файл cookie в браузере:
РЕДАКТИРОВАТЬ — Почти работает: я добавил в API идентификации в startup.cs это:
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
builder.SetIsOriginAllowed(_ => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
И в почтовом коде Js:
// POST ----------------------------------------------------------------------------------
fetch(identityApiUri postUrl formQueryString, // #1 = API-Address, #2 = API - Controller/Mehod, #3 = form data as sring
{
method: 'POST',
mode: 'no-cors',
headers: {
'Access-Control-Allow-Origin': '*'
},
credentials: 'include'
И теперь я получаю файл cookie, но я также получаю исключение:
Сервер возвращает код состояния 200, и теперь я могу получить файл cookie, но я получаю исключение в методе fetch api post.
Хорошо: ложь — но я получаю файл cookie, и сервер возвращает статус 200.
Ответ №1:
Вы звоните fetch()
из другого источника, чем api, верно? Если это так, то это звучит как простая проблема CORS.
По умолчанию CORS не включает учетные данные, такие как файлы cookie. Вы должны зарегистрироваться, установив режим учетных данных на стороне клиента и Access-Control-Allow-Credentials
заголовок на стороне сервера.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
С fetch()
помощью api режим учетных данных устанавливается с credentials: 'include'
помощью опции. Что касается серверной стороны, я не знаком с ASP, но похоже, что он предоставляет какой-то удобный метод для установки соответствующего заголовка.
Как вы намекаете в своем посте, когда в Access-Control-Allow-Credentials
заголовке установлено значение true
, *
значение, означающее любое происхождение, на самом деле не может использоваться в Access-Control-Allow-Origin
заголовке, поэтому вам нужно будет четко указать, какое происхождение вы хотите разрешить, т. Е. Происхождение вашего клиентского приложения, происхождение определяется как комбинация протокола, домена и порта.
Комментарии:
1. Я не уверен, правильно ли я понимаю CORS. Я хочу использовать API идентификации непосредственно из клиента, которым является приложение SPA. Но если API хочет, чтобы я сказал, какое происхождение, я не могу знать, потому что клиентская сторона может быть доступна любому. Должен ли я использовать Клиентский сервер в качестве «посредника» для передачи данных в API идентификации и указания их источника? Но если я это сделаю, не возникнет ли у меня та же проблема «CORS» с клиентским сервером — потому что все, кто может получить доступ к Клиенту, могут отправлять данные, и это опять же любое происхождение?
2. «Но если API хочет, чтобы я сказал, какое происхождение, я не могу знать, потому что Клиентская сторона может быть доступна любому». Тот факт, что любой может получить доступ к клиентскому приложению, не влияет на источник клиентского приложения, который всегда будет одним и тем же, независимо от того, как определяется комбинацией протокола, домена и порта. Имеет ли это смысл? Или вы на самом деле хотите сказать, что несколько клиентских приложений, доступных в разных источниках, могут захотеть получить доступ к api?
3. Хорошо, теперь я вроде как понимаю. Итак, вы имеете в виду, что мне просто нужно добавить адрес клиентского приложения в CORS в API идентификации, чтобы разрешить это происхождение.
4. Да, настройте свой сервер так, чтобы он возвращал значение источника вашего клиентского приложения в качестве значения
Access-Control-Allow-Origin
заголовка.5. Примечание. Я только что понял, что мой первоначальный ответ был немного усечен, просто отредактировал его. Эта часть: «Что касается серверной части, я не знаком с ASP, но похоже, что он предоставляет какой-то удобный метод для установки соответствующего заголовка».
Ответ №2:
Теперь это работает благодаря @IAmDranged
Метод JS — Fetch Api — Post:
fetch(identityApiUri postUrl formQueryString, // #1 = API-Address, #2 = API - Controller/Mehod, #3 = form data as sring
{
method: 'POST',
mode: 'cors',
//headers: {
// 'Access-Control-Allow-Origin': 'https://localhost',
//},
credentials: 'include'
}).then(function (response)
{ ...........................
API идентификации Asp Net core — Startup.cs:
// Configure ===========================================================================================
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
// CORS
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.WithOrigins("https://localhost:44351")); // Allow only this origin
//.SetIsOriginAllowed(origin => true));// Allow any origin
.................................................
Так что решение было:
- Добавить в метод JS — выборки:
mode: 'cors', credentials: 'include'
- Добавить в Asp net Core — startup.cs
пользователи приложений(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.WithOrigins("https://localhost:44351")); // Allow only this origin
//.SetIsOriginAllowed(origin => true));// Allow any origin
.WithOrigins("https://localhost:44351"));
Позволяет только клиенту использовать api, а если я использую .SetIsOriginAllowed(origin => true));
без WithOrigins
части — это позволяет всем.