Я никогда раньше не работал с ASP.NET Core. Пока я просто возюсь, у меня есть желание сделать свою авторизацию с помощью JWT в ASP.NET Core MVC. В итоге я посмотрел несколько гайдов, прочитал несколько статей, возможно я чего-то не понял, но у меня почему-то не работает авторизация, хотя я получаю токен, я сразу делаю запрос к контроллеру с полем Authorize и получите ответ
Ошибка носителя = "invalid_token"
Получите токен, затем скопируйте токен и выполните метод публикации, требующий авторизации
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using MySteamDBMetacritic.Db;
using MySteamDBMetacritic.Models;
using MySteamDBMetacritic.ViewModels;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Runtime.Intrinsics.Arm;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
namespace MySteamDBMetacritic.Controllers
{
public class AccountController : Controller
{
private readonly ILogger _logger;
private readonly ApplicationDbContext _context;
private readonly UserManager _userManager;
private readonly SignInManager _signInManager;
private readonly IConfiguration _configuration;
public AccountController(ILogger logger, ApplicationDbContext context, UserManager userManager, SignInManager signInManager, IConfiguration configuration)
{
_context = context;
_logger = logger;
_userManager = userManager;
_signInManager = signInManager;
_configuration = configuration;
}
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
public async Task Register([FromBody]RegisterViewModel model)
{
if (ModelState.IsValid)
{
User user = new User { Email = model.Email, UserName = model.UserName };
if (_userManager.Users
.FirstOrDefault(x => x.Email == user.Email) != default(User))
{
return View(model);
}
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_context.SaveChanges();
await _signInManager.SignInAsync(user, false);
return Json(new { token = Token(model.UserName, model.Password), returnUrl = Url.Action("Index", "Game") });
}
else
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
}
return View(model);
}
[HttpGet]
public IActionResult Login(string returnUrl = null)
{
return View(new LoginViewModel { ReturnUrl = returnUrl });
}
[HttpPost]
public async Task Login([FromBody]LoginViewModel model)
{
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, false);
if (result.Succeeded)
{
if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
{
return Json(new { token = ((JsonResult)Token(model.UserName, model.Password)).Value, returnUrl = model.ReturnUrl});
}
else
{
return Json(new { token = ((JsonResult)Token(model.UserName, model.Password)).Value, returnUrl = Url.Action("Index", "Game") });
}
}
else
{
ModelState.AddModelError("", "Incorrect username or password");
}
}
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Logout()
{
await _signInManager.SignOutAsync();
return RedirectToAction("Index", "Game");
}
public async Task ChangePassword(int id)
{
User user = await _userManager.FindByIdAsync(id.ToString());
if (user == null)
{
return NotFound();
}
ChangePasswordViewModel model = new ChangePasswordViewModel { Id = user.Id, Email = user.Email };
return View(model);
}
[HttpPost]
public async Task ChangePassword(ChangePasswordViewModel model)
{
if (ModelState.IsValid)
{
User user = await _userManager.FindByIdAsync(model.Id.ToString());
if (user != null)
{
var _passwordValidator = HttpContext.RequestServices.GetService(typeof(IPasswordValidator)) as IPasswordValidator;
var _passwordHasher = HttpContext.RequestServices.GetService(typeof(IPasswordHasher)) as IPasswordHasher;
IdentityResult result = await _passwordValidator.ValidateAsync(_userManager, user, model.NewPassword);
if (result.Succeeded)
{
user.PasswordHash = _passwordHasher.HashPassword(user, model.NewPassword);
await _userManager.UpdateAsync(user);
return RedirectToAction("Index");
}
else
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
}
else
{
ModelState.AddModelError(string.Empty, "User is missing");
}
}
return View(model);
}
public IActionResult Token(string username, string password)
{
var identity = GetIdentity(username, password).GetAwaiter().GetResult();
if (identity == null)
{
return BadRequest(new { errorText = "Invalid username or password." });
}
var now = DateTime.Now;
var jwt = new JwtSecurityToken(
issuer: _configuration["Jwt:Issuer"],
audience: _configuration["Jwt:Audience"],
notBefore: now,
claims: identity.Claims,
expires: now.Add(TimeSpan.FromMinutes(double.Parse(_configuration["Jwt:ExpiresMinutes"]))),
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])), SecurityAlgorithms.HmacSha256));
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
access_token = encodedJwt,
username = identity.Name
};
return Json(response);
}
private async Task GetIdentity(string username, string password)
{
User user = await _userManager.FindByNameAsync(username);
if (user != null)
{
var claims = new List
{
new Claim(ClaimsIdentity.DefaultNameClaimType, user.UserName),
new Claim(ClaimsIdentity.DefaultRoleClaimType, string.Join(',',_userManager.GetRolesAsync(user).Result))
};
ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, "Token",
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
return claimsIdentity;
}
return null;
}
}
}
С EF раньше не работал, он свои таблицы создавал, насколько я понимаю, некоторые здесь тоже используются для авторизации, возможно проблема в том, что он ничего не сохраняет есть, но почему-то во всех гайдах, которые я смотрел, все как-то работает.
Думаю, не стоит выкладывать сюда весь код, но если вдруг понадобится что-то посмотреть , вот он https://github.com/Drobovik04/MySteamDBMetacritic
Также еще один вопрос - есть ли какой-то простой механизм отправки токена в запросах, а то почему-то есть много мест где привязывают JS к представлению и завязывают на нажатие кнопок на форме, потом описываем запрос через fetch, меня просто очень смущает такая реализация, наверное есть более удобный и правильный способ
Попытка: переписать код
Ожидается: успешная авторизация p>
Результат: токен получен, сохранен, но ошибка носителя
Я никогда раньше не работал с ASP.NET Core. Пока я просто возюсь, у меня есть желание сделать свою авторизацию с помощью JWT в ASP.NET Core MVC. В итоге я посмотрел несколько гайдов, прочитал несколько статей, возможно я чего-то не понял, но у меня почему-то не работает авторизация, хотя я получаю токен, я сразу делаю запрос к контроллеру с полем Authorize и получите ответ
Ошибка носителя = "invalid_token"
Получите токен, затем скопируйте токен и выполните метод публикации, требующий авторизации [code]POST https://localhost:7081/Account/Login Content-Type: application/json
{ "UserName": "Admin", "Password": "aA12345!" }
### GET https://localhost:7081/Users/Index Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiQWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbiIsIm5iZiI6MTcyMDk1MDc1NSwiZXhwIjoxNzIwOTU0MzU1LCJpc3MiOiJNeVN0ZWFtREJEYXRhYmFzZSIsImF1ZCI6Ik15U3RlYW1EQkRhdGFiYXNlIn0.lgXz-1z7CJwJiJsqjn_q7WMQy6-rZ_EvbYlEDh9JrGk [/code] Код контроллера [code]using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using MySteamDBMetacritic.Db; using MySteamDBMetacritic.Models; using MySteamDBMetacritic.ViewModels; using System; using System.IdentityModel.Tokens.Jwt; using System.Runtime.Intrinsics.Arm; using System.Security.Claims; using System.Security.Cryptography; using System.Text;
[HttpGet] public IActionResult Register() { return View(); }
[HttpPost] public async Task Register([FromBody]RegisterViewModel model) { if (ModelState.IsValid) { User user = new User { Email = model.Email, UserName = model.UserName };
[HttpPost] public async Task Login([FromBody]LoginViewModel model) { if (ModelState.IsValid) { var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, false);
public async Task ChangePassword(int id) { User user = await _userManager.FindByIdAsync(id.ToString());
if (user == null) { return NotFound(); }
ChangePasswordViewModel model = new ChangePasswordViewModel { Id = user.Id, Email = user.Email };
return View(model); }
[HttpPost] public async Task ChangePassword(ChangePasswordViewModel model) { if (ModelState.IsValid) { User user = await _userManager.FindByIdAsync(model.Id.ToString());
if (user != null) { var _passwordValidator = HttpContext.RequestServices.GetService(typeof(IPasswordValidator)) as IPasswordValidator; var _passwordHasher = HttpContext.RequestServices.GetService(typeof(IPasswordHasher)) as IPasswordHasher;
IdentityResult result = await _passwordValidator.ValidateAsync(_userManager, user, model.NewPassword);
public IActionResult Token(string username, string password) { var identity = GetIdentity(username, password).GetAwaiter().GetResult();
if (identity == null) { return BadRequest(new { errorText = "Invalid username or password." }); }
var now = DateTime.Now;
var jwt = new JwtSecurityToken( issuer: _configuration["Jwt:Issuer"], audience: _configuration["Jwt:Audience"], notBefore: now, claims: identity.Claims, expires: now.Add(TimeSpan.FromMinutes(double.Parse(_configuration["Jwt:ExpiresMinutes"]))), signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])), SecurityAlgorithms.HmacSha256)); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new { access_token = encodedJwt, username = identity.Name };
return Json(response); }
private async Task GetIdentity(string username, string password) { User user = await _userManager.FindByNameAsync(username);
if (user != null) { var claims = new List { new Claim(ClaimsIdentity.DefaultNameClaimType, user.UserName), new Claim(ClaimsIdentity.DefaultRoleClaimType, string.Join(',',_userManager.GetRolesAsync(user).Result)) };
ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, "Token", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
return claimsIdentity; }
return null; } } } [/code] С EF раньше не работал, он свои таблицы создавал, насколько я понимаю, некоторые здесь тоже используются для авторизации, возможно проблема в том, что он ничего не сохраняет есть, но почему-то во всех гайдах, которые я смотрел, все как-то работает. Думаю, не стоит выкладывать сюда весь код, но если вдруг понадобится что-то посмотреть , вот он https://github.com/Drobovik04/MySteamDBMetacritic Также еще один вопрос - есть ли какой-то простой механизм отправки токена в запросах, а то почему-то есть много мест где привязывают JS к представлению и завязывают на нажатие кнопок на форме, потом описываем запрос через fetch, меня просто очень смущает такая реализация, наверное есть более удобный и правильный способ [list] [*]Попытка: переписать код
[*]Ожидается: успешная авторизация p>
[*]Результат: токен получен, сохранен, но ошибка носителя