diff --git a/Cloud/Cloud.csproj b/Cloud/Cloud.csproj
index d9f0064..93e4190 100644
--- a/Cloud/Cloud.csproj
+++ b/Cloud/Cloud.csproj
@@ -7,6 +7,8 @@
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -14,6 +16,7 @@
+
diff --git a/Cloud/Controllers/AuthController.cs b/Cloud/Controllers/AuthController.cs
new file mode 100644
index 0000000..c63a89c
--- /dev/null
+++ b/Cloud/Controllers/AuthController.cs
@@ -0,0 +1,104 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Identity;
+using Cloud.Models;
+using Cloud.Requests;
+using Microsoft.IdentityModel.Tokens;
+using System.IdentityModel.Tokens.Jwt;
+using System.Text;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.AspNetCore.Authorization;
+using System.Security.Claims;
+
+namespace Cloud.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class AuthController : ControllerBase
+{
+ private PasswordHasher _passwordHasher;
+ private IConfiguration _config;
+ private ApplicationContext _context;
+
+ public AuthController(IConfiguration config, ApplicationContext context)
+ {
+ _passwordHasher = new PasswordHasher();
+ _config = config;
+ _context = context;
+ }
+
+ [HttpPost("register")]
+ public async Task Register([FromBody] RegisterRequest request)
+ {
+ var existUser = await _context.Users.SingleOrDefaultAsync(u => u.Email == request.Email);
+
+ if (existUser != null) {
+ return BadRequest("Пользователь с такой эл. почтой уже существует");
+ }
+
+ var user = new User
+ {
+ Name = request.Name,
+ Email = request.Email,
+ Password = _passwordHasher.HashPassword(null, request.Password)
+ };
+
+ _context.Users.Add(user);
+ await _context.SaveChangesAsync();
+
+ return Ok("Пользователь успешно зарегистрирован");
+ }
+
+ [HttpPost("login")]
+ public async Task Login([FromBody] LoginRequest request)
+ {
+ var user = await _context.Users.SingleOrDefaultAsync(u => u.Email == request.Email);
+
+ if (user == null) {
+ return Unauthorized("Пользователя с такой эл. почтой не существует");
+ }
+
+ var verificationResult = _passwordHasher.VerifyHashedPassword(null, user.Password, request.Password);
+
+ if (verificationResult == PasswordVerificationResult.Failed) {
+ return Unauthorized("Неверный пароль");
+ }
+
+ var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
+ var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
+
+ var claims = new[]
+ {
+ new Claim(ClaimTypes.Name, user.Email),
+ };
+
+ var Sectoken = new JwtSecurityToken(_config["Jwt:Issuer"],
+ _config["Jwt:Issuer"],
+ claims: claims,
+ expires: DateTime.Now.AddMinutes(120),
+ signingCredentials: credentials);
+
+ var token = new JwtSecurityTokenHandler().WriteToken(Sectoken);
+
+ return Ok(token);
+ }
+
+ [Authorize]
+ [HttpGet("user")]
+ public async Task GetAuthUser()
+ {
+ var userEmail = User.Identity.Name;
+
+ var user = await _context.Users.SingleOrDefaultAsync(u => u.Email == userEmail);
+
+ if (user == null) {
+ return NotFound("Пользователь не найден");
+ }
+
+ return Ok(new
+ {
+ user.Id,
+ user.Name,
+ user.Email
+ });
+ }
+}
\ No newline at end of file
diff --git a/Cloud/Program.cs b/Cloud/Program.cs
index 2b9379b..1c10d82 100644
--- a/Cloud/Program.cs
+++ b/Cloud/Program.cs
@@ -1,17 +1,73 @@
using Cloud;
using Microsoft.EntityFrameworkCore;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+using System.Text;
+using FluentValidation;
+using FluentValidation.AspNetCore;
+using Cloud.Validation;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
+//Jwt configuration starts here
+var jwtIssuer = builder.Configuration.GetSection("Jwt:Issuer").Get();
+var jwtKey = builder.Configuration.GetSection("Jwt:Key").Get();
+
+builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ .AddJwtBearer(options =>
+ {
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidateLifetime = true,
+ ValidateIssuerSigningKey = true,
+ ValidIssuer = jwtIssuer,
+ ValidAudience = jwtIssuer,
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
+ };
+ });
+
builder.Services.AddDbContext(options =>
options.UseNpgsql("Host=localhost;Port=5438;Database=main_database;Username=postgres;Password=12345"));
builder.Services.AddControllers();
+builder.Services.AddFluentValidationAutoValidation();
+builder.Services.AddFluentValidationClientsideAdapters();
+builder.Services.AddValidatorsFromAssemblyContaining();
+builder.Services.AddValidatorsFromAssemblyContaining();
+
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen();
+builder.Services.AddSwaggerGen(c =>
+{
+ c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "Cloud API", Version = "v1" });
+
+ c.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
+ {
+ Description = "Введите ваш Bearer токен",
+ Name = "Authorization",
+ In = Microsoft.OpenApi.Models.ParameterLocation.Header,
+ Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey
+ });
+
+ c.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
+ {
+ {
+ new Microsoft.OpenApi.Models.OpenApiSecurityScheme
+ {
+ Reference = new Microsoft.OpenApi.Models.OpenApiReference
+ {
+ Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
+ Id = "Bearer"
+ }
+ },
+ new string[] {}
+ }
+ });
+});
var app = builder.Build();
@@ -19,11 +75,17 @@ var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
- app.UseSwaggerUI();
+ app.UseSwaggerUI(c =>
+ {
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "Cloud API V1");
+ c.RoutePrefix = string.Empty;
+ });
}
app.UseHttpsRedirection();
+app.UseAuthentication();
+
app.UseAuthorization();
app.MapControllers();
diff --git a/Cloud/Requests/LoginRequest.cs b/Cloud/Requests/LoginRequest.cs
new file mode 100644
index 0000000..472da63
--- /dev/null
+++ b/Cloud/Requests/LoginRequest.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Cloud.Requests;
+
+public class LoginRequest
+{
+ public string Email { get; set; }
+
+ public string Password { get; set; }
+}
\ No newline at end of file
diff --git a/Cloud/Requests/RegisterRequest.cs b/Cloud/Requests/RegisterRequest.cs
new file mode 100644
index 0000000..a92e7a8
--- /dev/null
+++ b/Cloud/Requests/RegisterRequest.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Cloud.Requests;
+
+public class RegisterRequest
+{
+ public string Name { get; set; }
+
+ public string Email { get; set; }
+
+ public string Password { get; set; }
+}
\ No newline at end of file
diff --git a/Cloud/Validation/LoginValidator.cs b/Cloud/Validation/LoginValidator.cs
new file mode 100644
index 0000000..19f6ec0
--- /dev/null
+++ b/Cloud/Validation/LoginValidator.cs
@@ -0,0 +1,18 @@
+using Cloud.Requests;
+using FluentValidation;
+
+namespace Cloud.Validation;
+
+public class LoginValidator : AbstractValidator
+{
+ public LoginValidator()
+ {
+ RuleFor(request => request.Email)
+ .NotEmpty().WithMessage("Email обязателен для заполнения")
+ .EmailAddress().WithMessage("Некорректный формат Email");
+
+ RuleFor(request => request.Password)
+ .NotEmpty().WithMessage("Пароль обязателен для заполнения")
+ .MinimumLength(8).WithMessage("Пароль должен быть не менее 8 символов");
+ }
+}
diff --git a/Cloud/Validation/RegisterValidator.cs b/Cloud/Validation/RegisterValidator.cs
new file mode 100644
index 0000000..1cfcb8d
--- /dev/null
+++ b/Cloud/Validation/RegisterValidator.cs
@@ -0,0 +1,22 @@
+using Cloud.Requests;
+using FluentValidation;
+
+namespace Cloud.Validation;
+
+public class RegisterValidator : AbstractValidator
+{
+ public RegisterValidator()
+ {
+ RuleFor(user => user.Name)
+ .NotEmpty().WithMessage("Имя обязательно для заполнения")
+ .MaximumLength(50).WithMessage("Имя должно быть не более 50 символов");
+
+ RuleFor(user => user.Email)
+ .NotEmpty().WithMessage("Email обязателен для заполнения")
+ .EmailAddress().WithMessage("Некорректный формат Email");
+
+ RuleFor(user => user.Password)
+ .NotEmpty().WithMessage("Пароль обязателен для заполнения")
+ .MinimumLength(8).WithMessage("Пароль должен быть не менее 8 символов");
+ }
+}
\ No newline at end of file
diff --git a/Cloud/appsettings.json b/Cloud/appsettings.json
index 10f68b8..b272a9c 100644
--- a/Cloud/appsettings.json
+++ b/Cloud/appsettings.json
@@ -5,5 +5,9 @@
"Microsoft.AspNetCore": "Warning"
}
},
- "AllowedHosts": "*"
-}
+ "AllowedHosts": "*",
+ "Jwt": {
+ "Key": "m7TyhE20s0dVtUDAr9EnFdPZnAG8maxgBTaiW5j6kO6RQhWDAGxYmXyu0suDnE0o",
+ "Issuer": "localhost"
+ }
+}
\ No newline at end of file