Я разрабатываю проект ASP.NET Core 8.0. В своем проекте я использую архитектуру Onion. В качестве безопасности я использую JWT и Identity. Когда я генерирую токены на стороне API и отправляю запрос от почтальона, я могу получить доступ к данным, поэтому все работает гладко. На стороне MVC я выполняю процесс входа в систему и получаю доступ к данным. Однако есть такая проблема. Если я использую следующую конфигурацию в Program.cs, она перенаправляется на страницу «Учетная запись/Вход» вместо «Вход/Индекс». Это не индекс, который я определил.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddCookie(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Login/Index";
options.LogoutPath = "/Login/Index";
options.AccessDeniedPath = "/Error/Error404";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.Name = "AuthToken";
});
Если я использую его таким образом, он возвращается на страницу входа/индекса, но не входит в систему, он переходит на главную/индекс и снова возвращается на страницу входа/индекса.
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.LoginPath = "/Login/Index";
options.LogoutPath = "/Login/Index";
options.AccessDeniedPath = "/Error/Error404";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.Name = "AuthToken";
});
Другие мои коды:
Вся конфигурация Program.cs:
using Domain.Entities;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Persistence.Context;
using PresentationUI.Handlers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ApplicationContext>();
builder.Services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationContext>()
.AddDefaultTokenProviders();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddCookie(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Login/Index";
options.LogoutPath = "/Login/Index";
options.AccessDeniedPath = "/Error/Error404";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.Name = "AuthToken";
});
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
builder.Services.AddTransient<AuthorizedHttpClientHandler>();
builder.Services.AddHttpClient("AuthorizedClient")
.AddHttpMessageHandler<AuthorizedHttpClientHandler>();
builder.Services.AddControllersWithViews();
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/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.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Логинконтроллер.cs:
using Domain.Entities;
using DtoLayer.LoginDtos;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using PresentationUI.Models;
using System.Text.Json;
namespace PresentationUI.Controllers
{
public class LoginController : Controller
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly UserManager<AppUser> _userManager;
private readonly SignInManager<AppUser> _signInManager;
public LoginController(IHttpClientFactory httpClientFactory, UserManager<AppUser> userManager, SignInManager<AppUser> signInManager)
{
_httpClientFactory = httpClientFactory;
_userManager = userManager;
_signInManager = signInManager;
}
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Index(CreateLoginDto createLoginDto)
{
var user = await _userManager.FindByNameAsync(createLoginDto.UserName);
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, createLoginDto.Password, true);
if (result.Succeeded)
{
if (!await _userManager.IsEmailConfirmedAsync(user))
{
return RedirectToAction("Index", "Confirmation");
}
else
{
var login = await _signInManager.PasswordSignInAsync(createLoginDto.UserName, createLoginDto.Password, true, true);
if (login.Succeeded)
{
if (await _userManager.IsInRoleAsync(user, "User"))
{
var client = _httpClientFactory.CreateClient();
var content = new StringContent(JsonSerializer.Serialize(createLoginDto), System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://localhost:7125/api/Login", content);
if (response.IsSuccessStatusCode)
{
var jsonData = await response.Content.ReadAsStringAsync();
var tokenModel = JsonSerializer.Deserialize<JwtResponseModel>(jsonData, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
if (tokenModel != null && tokenModel.Token != null)
{
HttpContext.Response.Cookies.Append("AuthToken", tokenModel.Token, new CookieOptions
{
HttpOnly = true,
Secure = true,
});
return RedirectToAction("Index", "Home");
}
}
}
else
{
ModelState.AddModelError("", "Bu sayfaya erişim izniniz bulunmamaktadır.");
return View();
}
}
else if (login.IsLockedOut)
{
ModelState.AddModelError("", "Fazla sayıda hatalı giriş yaptığınız için hesabınız kilitlendi. Lütfen daha sonra tekrar deneyiniz. Şifrenizi hatırlamıyorsanız 'Şifremi Unuttum' kısmından yeni bir şifre belirleyebilirsiniz.");
}
else
{
ModelState.AddModelError("", "Kullanıcı Adı veya Şifre Hatalı");
}
}
}
else if (result.IsLockedOut)
{
ModelState.AddModelError("", "Fazla sayıda hatalı giriş yaptığınız için hesabınız kilitlendi. Lütfen daha sonra tekrar deneyiniz. Şifrenizi hatırlamıyorsanız 'Şifremi Unuttum' kısmından yeni bir şifre belirleyebilirsiniz.");
}
else
{
ModelState.AddModelError("", "Kullanıcı Adı veya Şifre Hatalı");
}
}
else
{
ModelState.AddModelError("", "Böyle Bir Hesap Bulunamadı");
}
return View();
}
}
}
Хоумконтроллер.cs:
using DtoLayer.BrandDtos;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace PresentationUI.Controllers
{
[Authorize]
public class HomeController : Controller
{
private readonly IHttpClientFactory _clientFactory;
public HomeController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<IActionResult> Index()
{
var client = _clientFactory.CreateClient("AuthorizedClient");
var response = await client.GetAsync("https://localhost:7125/api/Brand");
if (response.IsSuccessStatusCode)
{
var jsonData = await response.Content.ReadAsStringAsync();
var values = JsonConvert.DeserializeObject<List<ResultBrandDto>>(jsonData);
return View(values);
}
return View();
}
}
}
АвторизованныйHttpClientHandler.cs
using System.Net.Http.Headers;
namespace PresentationUI.Handlers
{
public class AuthorizedHttpClientHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor;
public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = _httpContextAccessor.HttpContext.Request.Cookies["AuthToken"];
if (!string.IsNullOrEmpty(token))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
return await base.SendAsync(request, cancellationToken);
}
}
}
В вашем проекте «MVC Front» на самом деле нет JwtBearerDefaults.AuthenticationScheme
. Эту схему jwt следует добавить только в проект «back api».
«AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddCookie(JwtBearerDefaults.AuthenticationScheme.options...» неверно. При этом создается еще один файл cookie с именем «jwt». Но это не настоящий JwtBearerDefaults.AuthenticationScheme
.
Вместо этого вы можете настроить файл cookie «Asp.Net Core Identity» следующим образом:
builder.Services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/Login/Index";
options.LogoutPath = "/Login/Index";
options.AccessDeniedPath = "/Error/Error404";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
// This name shouldn't be changed. The cookie "authtoken" is only for storage purpose. Not for authentication.
//options.Cookie.Name = "AuthToken";
});