-
Notifications
You must be signed in to change notification settings - Fork 0
Sprint Backend 04. Jwt
IOF edited this page Dec 13, 2025
·
5 revisions
- проверьте фиксацию изменений в master
- создайте ветку
sprint4
- добавьте сингнатуру в интерфейс IUserRepository и метод
User FindUserByLogin(string login) - удалите реализацию
UserMemoryRepository. Можно не удалять, но тогда надо реализовывать все новые методы интерфейса или делать заглушки - реализуйте логику метода в
UserRepository - создайте метод
POST /api/Users/LoginвUsersController.
[HttpPost("Login")]
public ActionResult<UserDto> Login(LoginDto loginDto)
{
var user = _userRepo.FindUserByLogin(loginDto.Login);
return CheckPasswordHash(loginDto, user);
}- проверку логики пароля вынесите в отдельный приватный метод, который поместите в
Users Controller
private ActionResult<UserDto> CheckPasswordHash(LoginDto loginDto, User user)
{
using var hmac = new HMACSHA256(user.PasswordSalt);
var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(loginDto.Password));
for (int i = 0; i < computedHash.Length; i++)
{
if (computedHash[i] != user.PasswordHash[i])
{
return Unauthorized($"Неправильный пароль");
}
}
return Ok(user.ToDto());
}- проверьте работу метода аутентификации по логину в swagger
- добавьте разрешения CORS для доступа к API
Program.cs
app.UseCors(o => o.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());- в проекте API создайте интерфейс
ITokenService
public interface ITokenService
{
string CreateToken(string UserLogin);
}- установите пакет
Microsoft.IdentityModel.TokensиSystem.IdentityModel.Tokens.Jwt:
<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.15.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.15.0" />
</ItemGroup>- реализуйте сервис
TokenServiceдля генерации jwt - токена в папкеServices.
public class TokenService : ITokenService
{
private readonly SymmetricSecurityKey _key;
public TokenService(IConfiguration config)
{
_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenPublicKey"]!));
}
public string CreateToken(string UserLogin)
{
var claims = new List<Claim>{
new Claim(JwtRegisteredClaimNames.Name, UserLogin)
};
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
var tokenDecriptor = new SecurityTokenDescriptor(){
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDecriptor);
return tokenHandler.WriteToken(token);
}
}Примечание: значение config["TokenPublicKey"] мы получаем из конфигурации файла appsettings.json. Это открытый ключ шифрования. Добавьте данную настройку после настроек логгирования.
appsettings.json
"TokenPublicKey":"tokentokentokentokentokentokentokentokentokentokentokentokentokentoken"- в модель
Userдобавьте новое свойствоpublic string Token {get; set;}. - в классе
SampleAppContextв методеOnModelCreatingобновите конфигурацию модели User:entity.Property(e => e.Token).IsRequired(); - создайте миграцию и примените для обновления базы данных.
- зарегистрируйте в контейнер DI сервис
TokenServicec областью видимостиScoped. - внедрите объект
ITokenServiceв конструкторUsersController. - примените метод генерации токена из сервиса к полю
Tokenу пользователя. - добавьте токен в модель
UserDto - обновите новым свойством маппер
UserMapper - проверьте конечную точку создания пользователя: у пользователя токен
- обновите
SeedsController, чтобы он создавал пользователей с токенами
- установите пакет
Microsoft.AspNetCore.Authentication.JwtBearer -v 9.0.0
SampleApp.API.csproj
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.0" />
</ItemGroup>
Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["TokenPublicKey"]!)),
};
});- добавьте новые параметры
AddSwaggerGen:
builder.Services.AddSwaggerGen(c =>
{
...
c.AddSecurityDefinition(
c.AddSecurityDefinition(
"Bearer",
new OpenApiSecurityScheme
{
Description = "Authorization using jwt token. Example: \"Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
}
);
c.AddSecurityRequirement(
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer",
},
},
new string[] { }
},
}
); "Bearer",
new OpenApiSecurityScheme
{
Description = "Authorization using jwt token. Example: \"Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
}
);
c.AddSecurityRequirement(
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer",
},
},
new string[] { }
},
}
);
});- зарегистрируйте сервис авторизации
builder.Services.AddAuthorization();, а затем подключите его в конвейер сразу после процесса аутентификации
app.UseAuthentication();
app.UseAuthorization();- для удобного тестирования jwt создайте отдельный
TokenController
[ApiController]
[Route("api/[controller]")]
public class TokenController : ControllerBase
{
private readonly IConfiguration _config;
public TokenController(IConfiguration config)
{
_config = config;
}
[HttpGet]
public IActionResult GenerateToken()
{
var claims = new List<Claim> { new Claim(ClaimTypes.Name, "user") };
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["TokenPublicKey"]!));
// создаем JWT-токен
var jwt = new JwtSecurityToken(
// issuer: _config["Jwt:Issuer"],
// audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.Add(TimeSpan.FromDays(365)),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
return Ok(new JwtSecurityTokenHandler().WriteToken(jwt));
}
}- чтобы защитить конечную точку надо поставить атрибут [Authorize]. Теперь, чтобы получить доступ по конечной точке надо передать в запросе заголовка
Authorizationвалидный jwt-токен формата:Bearer {token}. Проверить валидность токена можно на сайте - jwt.io. Тестирование удобно делать в Postman.
[Authorize]
[HttpGet]
public ActionResult GetUser(){
return Ok(_repo.GetUsers());
}
- отправьте запрос с jwt токеном в запросе с помощью Swagger
- введите в поле значение
Bearer token
Задание: протестируйте конечную точку создания пользователя в jwt-аутентификацией в Postman и Swagger
- создайте папку
Extensions, в которой создайте статический классJwtServices
public static class JwtServices
{
public static IServiceCollection AddJwtServices(
this IServiceCollection services,
IConfiguration configuration
)
{
services.AddSwaggerGen(c =>
{
c.EnableAnnotations();
c.SwaggerDoc(
"v1",
new OpenApiInfo
{
Title = "SampleApp",
Version = "v1",
Description = "API для пользователей",
Contact = new OpenApiContact
{
Url = new Uri("http://prep.scc/~asv"),
Email = "asv@prep.scc",
},
}
);
c.AddSecurityDefinition(
"Bearer",
new OpenApiSecurityScheme
{
Description = "Authorization using jwt token. Example: \"Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
}
);
c.AddSecurityRequirement(
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer",
},
},
new string[] { }
},
}
);
});
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(configuration["TokenPublicKey"]!)
),
};
});
services.AddAuthorization();
return services;
}
}Теперь мы вынесем эту логику в отдельный файл и можем подключить это все одним сервисом.
builder.Services.AddJwtServices(builder.Configuration);Примечание: настройте уровень логирования для отслеживания процесса работы с jwt в файле appsettings.json
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Authentication": "Debug"
}
}Замечание: быстрое генерирование jwt
dotnet user-jwts create
Для продакшена используйте ручную генерацию через System.IdentityModel.Tokens.Jwt.
Примечание: данные пользователя можно получать по токену
- git add -A
- git commit - m "Выполнен sprint4"
- git switch master
- git rebase sprint4