Добавил регистрацию и вход

This commit is contained in:
2026-06-15 03:32:39 +03:00
parent 15917d7c7e
commit 4dec3251a0
9 changed files with 131 additions and 21 deletions
+13 -10
View File
@@ -1,24 +1,27 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using URL_Shortener.Models;
using URL_Shortener.DTO; using URL_Shortener.DTO;
using URL_Shortener.Services;
namespace URL_Shortener.Controllers namespace URL_Shortener.Controllers
{ {
[ApiController] [ApiController]
[Route("user")] [Route("user")]
public class UserController(AppDbContext db) : ControllerBase public class UserController(UserService userService) : ControllerBase
{ {
private readonly AppDbContext _db = db; private readonly UserService _userService = userService;
[HttpGet("email")] [HttpPost("register")]
public async Task<IActionResult> GetUserByEmailAsync(string email) public async Task<IActionResult> RegisterAsync(RegisterUserDTO dto)
{ {
var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == email); await _userService.RegisterAsync(dto.Name, dto.Email, dto.Password);
if (user == null) return Ok();
return NotFound("User not found"); }
return Ok(user); [HttpPost("login")]
public async Task<IActionResult> LoginAsync(LoginUserDTO dto)
{
var jwt = await _userService.LoginAsync(dto.Email, dto.Password);
return Ok(jwt);
} }
} }
} }
+12
View File
@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace URL_Shortener.DTO
{
public class LoginUserDTO
{
[Required, EmailAddress]
public string Email { get; set; } = string.Empty;
[Required]
public string Password { get; set; } = string.Empty;
}
}
+14
View File
@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace URL_Shortener.DTO
{
public class RegisterUserDTO
{
[Required]
public string Name { get; set; } = string.Empty;
[Required, EmailAddress]
public string Email { get; set; } = string.Empty;
[Required]
public string Password { get; set; } = string.Empty;
}
}
+6 -1
View File
@@ -1,5 +1,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using URL_Shortener; using URL_Shortener;
using URL_Shortener.Services;
using URL_Shortener.Settings;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -12,9 +14,12 @@ builder.Services.AddOpenApi();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
// Чтобы короткие коды генерировать
builder.Services.AddSingleton<HashidsNet.Hashids>(); builder.Services.AddSingleton<HashidsNet.Hashids>();
builder.Services.AddScoped<UserService>();
builder.Services.AddScoped<JwtService>();
builder.Services.Configure<AuthSettings>(builder.Configuration.GetSection("AuthSettings"));
builder.Services.AddDbContext<AppDbContext>(options => builder.Services.AddDbContext<AppDbContext>(options =>
{ {
var connString = builder.Configuration.GetConnectionString("DefaultConnection"); var connString = builder.Configuration.GetConnectionString("DefaultConnection");
+32
View File
@@ -0,0 +1,32 @@
using URL_Shortener.Models;
using System.IdentityModel.Tokens.Jwt;
using URL_Shortener.Settings;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Text;
namespace URL_Shortener.Services
{
public class JwtService(IOptions<AuthSettings> options)
{
public string GenerateToken(User user)
{
var claims = new List<Claim>() {
new("userName", user.Name),
new("userEmail", user.Email),
new("id", user.Id.ToString())
};
var jwt = new JwtSecurityToken(
expires: DateTime.UtcNow.Add(options.Value.Expires),
claims: claims,
signingCredentials: new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.Value.SecretKey)),
SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
}
}
+34 -4
View File
@@ -1,10 +1,40 @@
namespace URL_Shortener.Services using URL_Shortener.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
namespace URL_Shortener.Services
{ {
public class UserService public class UserService(AppDbContext db, JwtService jwtService)
{ {
public async Task Register(string userName, string email, string hashedPassword) private readonly AppDbContext _db = db;
private readonly JwtService _jwtService = jwtService;
public async Task RegisterAsync(string userName, string email, string password, CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); if (await _db.Users.FirstOrDefaultAsync(u => u.Email == email, cancellationToken) != null)
throw new Exception("This user already exists.");
var user = new User()
{
Name = userName,
Email = email
};
user.HashedPassword = new PasswordHasher<User>().HashPassword(user, password);
await _db.Users.AddAsync(user, cancellationToken);
await _db.SaveChangesAsync(cancellationToken);
}
public async Task<string> LoginAsync(string email, string password, CancellationToken cancellationToken = default)
{
var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == email, cancellationToken) ??
throw new Exception("User not found.");
var res = new PasswordHasher<User>().VerifyHashedPassword(user, user.HashedPassword, password);
if (res == PasswordVerificationResult.Failed)
throw new Exception("Incorrect password.");
return _jwtService.GenerateToken(user);
} }
} }
} }
+8
View File
@@ -0,0 +1,8 @@
namespace URL_Shortener.Settings
{
public class AuthSettings
{
public string SecretKey { get; set; } = string.Empty;
public TimeSpan Expires { get; set; }
}
}
+8 -6
View File
@@ -11,19 +11,21 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Hashids.net" Version="1.7.0" /> <PackageReference Include="Hashids.net" Version="1.7.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.7" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7"> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.7"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.9">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.19.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+4
View File
@@ -8,5 +8,9 @@
"AllowedHosts": "*", "AllowedHosts": "*",
"ConnectionStrings": { "ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=appdb;Username=postgres;Password=app" "DefaultConnection": "Host=localhost;Database=appdb;Username=postgres;Password=app"
},
"AuthSettings": {
"SecretKey": "mysecretkeyqwertyuiopasdfghjklzxcvbnm",
"Expires": "00:01:00"
} }
} }