Yandex.Metrika Counter

Начать работать с нами легко! Просто заполните заявку, и мы свяжемся с вами для обсуждения деталей.

Бюджет

НаТимая Π½Π° ΠΊΠ½ΠΎΠΏΠΊΡƒ, Π²Ρ‹ Π΄Π°Ρ‘Ρ‚Π΅ согласиС Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ ΡΠΎΠ³Π»Π°ΡˆΠ°Π΅Ρ‚Π΅ΡΡŒ с ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΠΌ ΠΎ ΠΊΠΎΠ½Ρ„ΠΈΠ΄Π΅Π½Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π΄Π°Π½Π½Ρ‹Ρ….

ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ASP.NET Core Identity ΠΈ JWT Π² Β ASP.NET Core 3.1

Π’ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΉ ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ Ρ€Π°ΡΡΠΌΠΎΡ‚Ρ€Π΅Π»ΠΈ структуру JWT ΠΈ ΡΡ…Π΅ΠΌΡƒ Ρ€Π°Π±ΠΎΡ‚Ρ‹ ASP.NET Core Identity ΠΈ JWT Π²ΠΌΠ΅ΡΡ‚Π΅. Π’ ΡΡ‚ΠΎΠΉ β€” Π½Π° ΠΆΠΈΠ²ΠΎΠΌ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅ΠΌ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΡŽ Π² ASP.NET Core 3.1 с ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ этих Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΉ.

АрхитСктура прилоТСния ΠΈ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ nugget ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹

Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΎΠ΄Π΅ΠΌΠΎΠ½ΡΡ‚Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ΅ использованиС ASP.NET Core Identity ΠΈ JWT, я ΡΠΎΠ·Π΄Π°Π»Π° ΠΏΡΡ‚ΡŒ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ², настроила связи ΠΌΠ΅ΠΆΠ΄Ρƒ Π½ΠΈΠΌΠΈ ΠΈ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΠ»Π° Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ nugget-ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹. Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΠ»Π° Entity Framework для Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡ‚вия MS SQL Server.Β 

Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° доступСн Π½Π° GitHub

АрхитСктура прилоТСния: основныС ΠΌΠΎΠ΄ΡƒΠ»ΠΈ ΠΈ ΠΈΡ… nuget-ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹

ΠœΠΎΡ‘ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ β€” максимально ΡƒΠΏΡ€ΠΎΡ‰Π΅Π½Π½ΠΎΠ΅. Но Ρ Π²ΡΠ΅ Ρ€Π°Π²Π½ΠΎ ΠΏΡ€ΠΈΠ΄Π΅Ρ€ΠΆΠΈΠ²Π°Π»Π°ΡΡŒ Onion Architecture. Π’Π°ΠΊ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ получаСтся максимально ΠΏΡ€ΠΈΠ±Π»ΠΈΠΆΠ΅Π½Π½Ρ‹ΠΌ ΠΊ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌΡƒ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρƒ: Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π±Ρ€Π°Ρ‚ΡŒ ΠΈ Ρ€Π°ΡΡˆΠΈΡ€ΡΡ‚ΡŒ Π΄ΠΎ Β ΡΠ²ΠΎΠ΅Π³ΠΎ Ρ€Π°Π±ΠΎΡ‡Π΅Π³ΠΎ ΠΏΡ€ΠΎΡΡ‚Π΅Π½ΡŒΠΊΠΎΠ³ΠΎ Π²Π΅Π±-прилоТСния.Β 

НастраиваСм ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ

Π”ΠΎΠ±Π°Π²ΠΈΠΌ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ для ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½ΠΈΡ DataContext EntityFramework.

services.AddDbContext<DataContext>(opt =>
  opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 

Β 

И строку ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΊ MS SQL Server Π² appsettings.json.Π΅.

"ConnectionStrings": {
  "DefaultConnection": "Server=.\\SQL_2016;Database=AspNetCoreIdentity;user id=testUser;   
    password='testUser'"}

Β 

Π”ΠΎΠ±Π°Π²ΠΈΠΌ MediatR, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² Π½Π°ΡˆΠ΅ΠΌ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ CQRS. Он ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ‚ ΠΎΡ‚Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΊΠΎΠ΄, ΠΈΠ·ΠΌΠ΅Π½ΡΡŽΡ‰ΠΈΠΉ состояниС, ΠΎΡ‚ ΠΊΠΎΠ΄Π°, Ρ‡ΠΈΡ‚Π°ΡŽΡ‰Π΅Π³ΠΎ это ΡΠΎΡΡ‚ояниС. Π‘ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΎΠΌ CQRS ΠΌΡ‹ ΡƒΠΌΠ΅Π½ΡŒΡˆΠ°Π΅ΠΌ количСство зависимостСй Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ классС ΠΈ ΡΠΎΠ±Π»ΡŽΠ΄Π°Π΅ΠΌ SRP β€” ΠΎΠ΄ΠΈΠ½ ΠΈΠ· ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠΎΠ² SOLID. ΠŸΡ€ΠΎΠ΅ΠΊΡ‚, построСнный ΠΏΠΎ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠ°ΠΌ CQRS, Π»Π΅Π³Ρ‡Π΅ Ρ€Π°ΡΡˆΠΈΡ€ΡΡ‚ΡŒ ΠΈ Β Ρ‚Π΅ΡΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ.

services.AddMediatR(typeof(LoginHandler).Assembly);

Β 

ДобавляСм ASP.NET Core Identity Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚

ΠŸΠ΅Ρ€Π²Ρ‹ΠΌ Π΄Π΅Π»ΠΎΠΌ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π² Domain ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ класс AppUser.cs с Β Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹ΠΌΠΈ ΠΏΠΎΠ΄ Π½Π°ΡˆΠΈ Π·Π°Π΄Π°Ρ‡ΠΈ полями. УнаслСдуСм Π΅Π³ΠΎ ΠΎΡ‚ IdentityUser.

public class AppUser : IdentityUser
  {
      public string DisplayName { get; set; }
  }

Β 

Π’Π΅ΠΏΠ΅Ρ€ΡŒ зарСгистрируСм созданный Π½Π°ΠΌΠΈ класс ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π² Startup.cs.

var builder = services.AddIdentityCore<AppUser>();
var identityBuilder = new IdentityBuilder(builder.UserType, builder.Services);

Β 

Π”ΠΎΠ±Π°Π²ΠΈΠΌ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ EFData DataContext, унаслСдованный ΠΎΡ‚ Β IdentityDbContext.

public class DataContext: IdentityDbContext<AppUser> 
  {
      public DataContext(DbContextOptions options) : base(options) { }
  }

Β 

Установим Ρ‚ΠΈΠΏ Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π° (класс контСкста Π΄Π°Π½Π½Ρ‹Ρ…) Π² Startup.cs, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Identity Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ для Ρ…ранСния Π΄Π°Π½Π½Ρ‹Ρ….

identityBuilder.AddEntityFrameworkStores<DataContext> ();
identityBuilder.AddSignInManager<SignInManager<AppUser>>();

Β 

ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΠΌ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡŽ Π² ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ Configure() класса Startup.cs.

app.UseAuthentication();

Β 

Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½Π°Ρ‡Π°Π»ΡŒΠ½ΡƒΡŽ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΡŽ. Для ΡΡ‚ΠΎΠ³ΠΎ стартовым ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠΌ Π²Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ API, ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ Package Manager Console ΠΈ Π² Default project Π²Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ EFData. ЗапускаСм созданиС Π½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎΠΉ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:Β 

> Add-migration initial 

Β 

Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠΌ Π±ΡƒΠ΄Π΅Ρ‚ автоматичСски созданная миграция Π² ΠΏΠ°ΠΏΠΊΠ΅ Migrations ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° EFData. ΠŸΡ€Π΅ΠΆΠ΄Π΅ Ρ‡Π΅ΠΌ Π½Π°ΠΊΠ°Ρ‚ΠΈΡ‚ΡŒ Π΅Ρ‘ Π½Π° Π½Π°ΡˆΡƒ Π±Π°Π·Ρƒ, Π² Ρ‚ΠΎΠΌ ΠΆΠ΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ создадим DataSeed для Π°Π²Ρ‚озаполнСния Π½Π°Ρ‡Π°Π»ΡŒΠ½Ρ‹ΠΌΠΈ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ.

public class DataSeed
 {
      public static async Task SeedDataAsync(DataContext context, UserManager userManager)
      {
          if (!userManager.Users.Any())
          {
              var users = new List<AppUser>
                            {
                                new AppUser
                                    {
                                        DisplayName = "TestUserFirst",
                                        UserName = "TestUserFirst",
                                        Email = "testuserfirst@test.com"
                                    },

                                new AppUser
                                    {
                                        DisplayName = "TestUserSecond",
                                        UserName = "TestUserSecond",
                                        Email = "testusersecond@test.com"
                                    }
                              };

              foreach (var user in users)
              {
                  await userManager.CreateAsync(user, "qazwsX123@");
              }
          }
      }
  }

Β 

Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ Π±Π°Π·Ρƒ. Она Π·Π°ΠΏΠΎΠ»Π½ΠΈΡ‚ся Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π²ΠΎΠΌ запускС Π²Π΅Π±-прилоТСния.Β 

update-database

Β 

Π’ΠΎΡ‚ Ρ‡Ρ‚ΠΎ Ρƒ Π½Π°Ρ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ Π² Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅

Всё Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΠ΅ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Identity Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΎ. Π’Π΅ΠΏΠ΅Ρ€ΡŒ Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄ для Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ.

Π’ UserController ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° API Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄ LoginAsync.

[HttpPost("login")]
  public async Task<ActionResult<UserModel>> LoginAsync(LoginQuery query)
  {
       return await Mediator.Send(query);
  }
 


Π’ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ Application Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Ρ‚Ρ€ΠΈ ΠΊΠ»Π°ΡΡΠ°:

LoginHandler.cs

public class LoginHandler : IRequestHandler<LoginQuery, UserModel>
 {
	private readonly UserManager<AppUser> _userManager;

	private readonly SignInManager<AppUser> _signInManager;

	public LoginHandler(UserManager userManager<AppUser>,  
                                   SignInManager<AppUser> signInManager)
	{
		_userManager = userManager;
		_signInManager = signInManager;
	}

	public async Task<UserModel> Handle(LoginQuery request, CancellationToken cancellationToken)
	{
		var user = await _userManager.FindByEmailAsync(request.Email);
		if (user == null)
		{
			throw new RestException(HttpStatusCode.Unauthorized);
		}

		var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, false);

		if (result.Succeeded)
		{
			return new UserModel
						{
							DisplayName = user.DisplayName,
							Token = "test", (Π”Π°Π»Π΅Π΅ здСсь Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π·ΠΎΠ² ΠΌΠ΅Ρ‚ΠΎΠ΄Π° сСрвиса, Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΡŽΡ‰ΠΈΠΉ Token)                         
							UserName = user.UserName,
							Image = null
						};
		}

		throw new RestException(HttpStatusCode.Unauthorized);
	}
 }

Β 

LoginQuery.cs

public class LoginQuery : IRequest<UserModel>
{
	public string Email { get; set; }
	public string Password { get; set; }
}

Β 

LoginQueryValidation.cs

public class LoginQueryValidation : AbstractValidator<LoginQuery>
{
	public LoginQueryValidation()
	{
		RuleFor(x => x.Email).EmailAddress().NotEmpty();
		RuleFor(x => x.Password).NotEmpty();
	}
}

Β 

ΠŸΡ€ΠΎΡ‚Π΅ΡΡ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ любого API ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°. Π’ Π½Π°ΡˆΠ΅ΠΌ случаС это Β Π±ΡƒΠ΄Π΅Ρ‚ Postman. Π”Π΅Π»Π°Π΅ΠΌ запрос ΠΊ Π²Π΅Π±-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡŽ ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚:

Π’ Π·Π°ΠΏΡ€ΠΎΡΠ΅ Π½Π΅ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹Π΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Π΅ (ошибка Π² Π»ΠΎΠ³ΠΈΠ½Π΅ ΠΈΠ»ΠΈ ΠΏΠ°Ρ€ΠΎΠ»Π΅) β€” ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΎΡ‚Π²Π΅Ρ‚ 401 Unauthorized

Β 

На ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹ΠΉ запрос ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΎΡ‚Π²Π΅Ρ‚ 200 OK ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ

Π”ΠΎΠ±Π°Π²ΠΈΠΌ Π½Π°Π±ΠΎΡ€ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ Π² Startup.cs. Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ запрос ΠΊ Π½Π°ΡˆΠ΅ΠΌΡƒ API Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·ΠΎΠ²Π°Π½. ЕдинствСнный ΠΌΠ΅Ρ‚ΠΎΠ΄, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ ΡΠ²Π»ΡΡ‚ΡŒΡΡ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ΠΌ (Π² Π΄Π°Π»ΡŒΠ½Π΅ΠΉΡˆΠ΅ΠΌ ΠΈΡ… ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ большС) β€” это Β Login. Для ΡΡ‚ΠΎΠ³ΠΎ Π½Π° UserController навСсим Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ [AllowAnonymous].

services.AddMvc(option => 
{
// ΠžΡ‚ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ ΠΌΠ°Ρ€ΡˆΡ€ΡƒΡ‚ΠΈΠ·Π°Ρ†ΠΈΡŽ ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Ρ… Ρ‚ΠΎΡ‡Π΅ΠΊ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ endpoint-based logic ΠΈΠ· EndpointMiddleware
// ΠΈ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ использованиС ΠΌΠ°Ρ€ΡˆΡ€ΡƒΡ‚ΠΈΠ·Π°Ρ†ΠΈΠΈ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ IRouter. 
        option.EnableEndpointRouting = false;
        var policy = new AuthorizationPolicyBuilder()
                            .RequireAuthenticatedUser().RequireAuthenticatedUser().Build();
		option.Filters.Add(new AuthorizeFilter(policy));
	}).SetCompatibilityVersion(CompatibilityVersion.Latest);

ДобавляСм JWT Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚

ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ взаимодСйствия нашСго прилоТСния с JWT ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚ся достаточно просто. Π’ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Application ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ интСрфСйс IJwtGenerator с Π΅Π΄ΠΈΠ½ΡΡ‚Π²Π΅Π½Π½Ρ‹ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠΌ CreateToken.

 public interface IJwtGenerator
 {
     string CreateToken(AppUser user);
 }

Β 

Π’ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ Infrastructure Π΄ΠΎΠ±Π°Π²ΠΈΠΌ класс JwtGenerator ΠΈ ΡƒΠ½Π°ΡΠ»Π΅Π΄ΡƒΠ΅ΠΌ Π΅Π³ΠΎ ΠΎΡ‚ Β IJwtGenerator со ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ CreateToken:

        
	public class JwtGenerator : IJwtGenerator
	{
		private readonly SymmetricSecurityKey _key;

		public JwtGenerator(IConfiguration config)
		{
			_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"]));
		}

		public string CreateToken(AppUser user)
		{
			var claims = new List { new Claim(JwtRegisteredClaimNames.NameId, user.UserName) };

			var credentials = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);

			var tokenDescriptor = new SecurityTokenDescriptor
			{
				Subject = new ClaimsIdentity(claims),
				Expires = DateTime.Now.AddDays(7),
				SigningCredentials = credentials
			};
			var tokenHandler = new JwtSecurityTokenHandler();

			var token = tokenHandler.CreateToken(tokenDescriptor);

			return tokenHandler.WriteToken(token);
		}
	}


ЗарСгистрируСм Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ Π² ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π΅ прилоТСния Π² Startup.cs.

services.AddScoped<IJwtGenerator, JwtGenerator>();


Настроим ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ JWT Π² Startup.cs.

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("super secret key"));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(
        opt =>
             {
                 opt.TokenValidationParameters = new TokenValidationParameters
                                                     {
                                                         ValidateIssuerSigningKey = true,
                                                         IssuerSigningKey = key,
                                                         ValidateAudience = false,
                                                         ValidateIssuer = false,
                                                     };
             });	


Π—Π°Ρ‰ΠΈΡ‚Π° сСкрСтных ΠΊΠ»ΡŽΡ‡Π΅ΠΉ JWT
. БСйчас Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ строки ΡƒΠΊΠ°Π·Π°Π½ΠΎ Π² Β ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΎΠΌ Π²ΠΈΠ΄Π΅ ΠΈ Π±Π΅Ρ€Π΅Ρ‚ся Π΄Π°ΠΆΠ΅ Π½Π΅ ΠΈΠ· ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΎΠ². Π§Ρ‚ΠΎΠ±Ρ‹ бСзопасно Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ сСкрСтныС Π΄Π°Π½Π½Ρ‹Π΅, ΠΏΡ€Π΅Π΄Π»Π°Π³Π°ΡŽ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ .NET user secret. Π”ΠΎΠ±Π°Π²Π»ΡŽ лишь, Ρ‡Ρ‚ΠΎ ΠΈΡ‚ΠΎΠ³ΠΎΠ²Ρ‹ΠΉ Ρ„Π°ΠΉΠ» Ρ…Ρ€Π°Π½ΠΈΡ‚ Π½Π΅Π·Π°ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π³ΠΎ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π²ΠΎ Π²Ρ€Π΅ΠΌΡ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ. Π§Ρ‚ΠΎΠ±Ρ‹ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ сСкрСтноС Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡΡ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ Π² Package Manager Console.

> dotnet user-secrets init 


Π’ API.csproj Π΄ΠΎΠ±Π°Π²ΠΈΠΌ UserSecretsId.


Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π² Π½Π°ΡˆΠ΅ сСкрСтноС Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

> dotnet user-secrets set "TokenKey" "super secret key" –p API/


Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ всС, Ρ‡Ρ‚ΠΎ ΡΠ΅ΠΉΡ‡Π°Ρ находится Π² Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅, Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡΡ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

> dotnet user-secrets list –p API/


ПослС этого ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ наши сСкрСтныС Π΄Π°Π½Π½Ρ‹Π΅ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["TokenKey"]));


Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ JwtGenerator Π² LoginHandler:

 return new User
 {
        DisplayName = user.DisplayName,
        Token = _jwtGenerator.CreateToken(user),
	      UserName = user.UserName,
	      Image = null
 };


Π’Π°ΠΊ ΠΌΡ‹ ΡΠ²ΡΠ·Π°Π»ΠΈ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΡŽ с ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ASP.NET Core Identity ΠΈ Β Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΡŽ Ρ‚ΠΎΠΊΠ΅Π½Π° ΠΈΠ· JwtGenerator. Наш ΠΎΡ‚Π²Π΅Ρ‚ ΠΏΡ€ΠΈ Ρ€Π΅Π³ΠΈΡΡ‚Ρ€Π°Ρ†ΠΈΠΈ измСнится ΠΈ Π±ΡƒΠ΄Π΅Ρ‚ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ Ρ‚ΠΎΠΊΠ΅Π½, с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΠ΄Ρ‚ΠΈ Π½Π° ΡΠ΅Ρ€Π²Π΅Ρ€ Π·Π° Β Π΄Π°Π½Π½Ρ‹ΠΌΠΈ.

Β 

Β 

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ Ρ€Π°Π±ΠΎΡ‚Ρƒ Π²Π΅Π±-прилоТСния. Π§Ρ‚ΠΎΠ±Ρ‹ запрос выполнился ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ, Π² Β ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°Ρ… Headers Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ Authorization со Β Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ "Bearer {Ρ‚ΠΎΠΊΠ΅Π½}":

Β 

Π’ ΡΠ»ΡƒΡ‡Π°Π΅, Ссли Ρ‚ΠΎΠΊΠ΅Π½Π° Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚, вСрнСтся ошибка 401 Unauthorized

Π’ΠΎΡ‚ ΠΈ Π²ΡΡ‘! ΠœΡ‹ ΡΠΎΠ·Π΄Π°Π»ΠΈ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ Π² Π½Π΅Π³ΠΎ ASP.NET Core Identity ΠΈ Β JWT.

ΠŸΡ€Π΅ΠΈΠΌΡƒΡ‰Π΅ΡΡ‚Π²ΠΎ JWT Π² Π΅Π³ΠΎ ΡΠ°ΠΌΠΎΠ΄ΠΎΡΡ‚аточности. ВсС Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ для Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ Π΄Π°Π½Π½Ρ‹Π΅ хранятся Π² ΡΠ°ΠΌΠΎΠΌ Ρ‚ΠΎΠΊΠ΅Π½Π΅. JWT Π½Π΅ ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΠ²Π°Π΅Ρ‚ Π·Π°Ρ‰ΠΈΡ‚Ρƒ Π΄Π°Π½Π½Ρ‹Ρ…, Π½ΠΎ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΈΡ… ΠΏΠΎΠ΄Π»ΠΈΠ½Π½ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ Ρ†ΠΈΡ„Ρ€ΠΎΠ²ΠΎΠΉ подписи.


ΠŸΠΎΠ»Π΅Π·Π½Ρ‹Π΅ ссылки:

Π”ΠΆΠ΅Ρ„Ρ„Ρ€ΠΈ ΠŸΠ°Π»Π΅Ρ€ΠΌΠΎ ΠΎ Β«Π›ΡƒΠΊΠΎΠ²ΠΎΠΉ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π΅Β» (Π½Π° Π°Π½Π³Π».)

A naive introduction to CQRS in C#Β 

Safe storage of app secrets in development in ASP.NET Core

Β