Π ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠ΅ΠΉ ΡΡΠ°ΡΡΠ΅ ΠΌΡ ΡΠ°ΡΡΠΌΠΎΡΡΠ΅Π»ΠΈ ΡΡΡΡΠΊΡΡΡΡ JWT ΠΈ ΡΡ Π΅ΠΌΡ ΡΠ°Π±ΠΎΡΡ ASP.NET Core Identity ΠΈ JWT Π²ΠΌΠ΅ΡΡΠ΅. Π ΡΡΠΎΠΉ β Π½Π° ΠΆΠΈΠ²ΠΎΠΌ ΠΏΡΠΈΠΌΠ΅ΡΠ΅ ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅ΠΌ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΡ Π² ASP.NET Core 3.1 Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ΡΡΠΈΡ ΡΠ΅Ρ Π½ΠΎΠ»ΠΎΠ³ΠΈΠΉ.
ΠΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΈ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΡΠ΅ nugget ΠΏΠ°ΠΊΠ΅ΡΡ
Π§ΡΠΎΠ±Ρ ΠΏΡΠΎΠ΄Π΅ΠΌΠΎΠ½ΡΡΡΠΈΡΠΎΠ²Π°ΡΡ Π½Π° ΠΏΡΠ°ΠΊΡΠΈΠΊΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ASP.NET Core Identity ΠΈ JWT, Ρ ΡΠΎΠ·Π΄Π°Π»Π° ΠΏΡΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΡ
ΠΏΡΠΎΠ΅ΠΊΡΠΎΠ², Π½Π°ΡΡΡΠΎΠΈΠ»Π° ΡΠ²ΡΠ·ΠΈ ΠΌΠ΅ΠΆΠ΄Ρ Π½ΠΈΠΌΠΈ ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΠ»Π° Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΡΠ΅ nugget-ΠΏΠ°ΠΊΠ΅ΡΡ. ΠΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΠ»Π° Entity Framework Π΄Π»Ρ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΡ MS SQL Server.Β
ΠΡΡ
ΠΎΠ΄Π½ΡΠΉ ΠΊΠΎΠ΄ ΠΏΡΠΎΠ΅ΠΊΡΠ° Π΄ΠΎΡΡΡΠΏΠ΅Π½ Π½Π° GitHub
ΠΠΎΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ β ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎ ΡΠΏΡΠΎΡΠ΅Π½Π½ΠΎΠ΅. ΠΠΎ Ρ Π²ΡΠ΅ ΡΠ°Π²Π½ΠΎ ΠΏΡΠΈΠ΄Π΅ΡΠΆΠΈΠ²Π°Π»Π°ΡΡ 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. ΠΠ΅Π»Π°Π΅ΠΌ Π·Π°ΠΏΡΠΎΡ ΠΊ Π²Π΅Π±-ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΈ ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ:
Β
ΠΠΎΠ±Π°Π²ΠΈΠΌ Π½Π°Π±ΠΎΡ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠΉ Π² 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 {ΡΠΎΠΊΠ΅Π½}":
Β
ΠΠΎΡ ΠΈ Π²ΡΡ! ΠΡ ΡΠΎΠ·Π΄Π°Π»ΠΈ ΠΏΡΠΎΠ΅ΠΊΡ ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ Π² Π½Π΅Π³ΠΎ 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
Β