Skip to content

Commit fdd2b33

Browse files
author
bird_egop
committed
вторизация пользователя
1 parent aed85ad commit fdd2b33

File tree

9 files changed

+173
-3
lines changed

9 files changed

+173
-3
lines changed

BusinessLogic/BusinessLogic.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
1515
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
1616
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
17+
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.10.0" />
1718
</ItemGroup>
1819

1920
<ItemGroup>
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace BusinessLogic.Jwt;
2+
3+
public record JwtGenerationResult(string Token, string RefreshToken);
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System.IdentityModel.Tokens.Jwt;
2+
using System.Security.Claims;
3+
using System.Text;
4+
using BusinessLogic.Configs;
5+
using Microsoft.Extensions.Options;
6+
using Microsoft.IdentityModel.Tokens;
7+
8+
namespace BusinessLogic.Jwt;
9+
10+
public interface IJwtGeneratorService
11+
{
12+
JwtGenerationResult GenerateToken(Guid userId);
13+
}
14+
15+
public class JwtGeneratorService : IJwtGeneratorService
16+
{
17+
private readonly JwtConfig _config;
18+
19+
private static readonly JwtSecurityTokenHandler JwtSecurityTokenHandler = new();
20+
21+
public JwtGeneratorService(IOptions<JwtConfig> options)
22+
{
23+
_config = options.Value;
24+
}
25+
26+
public JwtGenerationResult GenerateToken(Guid userId)
27+
{
28+
var issuer = _config.Issuer;
29+
var audience = _config.Audience;
30+
31+
var key = Encoding.ASCII.GetBytes(_config.Key);
32+
33+
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256);
34+
35+
var tokenJwt = new JwtSecurityToken(
36+
issuer: issuer,
37+
audience: audience,
38+
notBefore: null,
39+
claims: new[]
40+
{
41+
new Claim(KnownJwtClaims.UserId, userId.ToString()),
42+
// the JTI is used for our refresh token which we will be covering in the next video
43+
new Claim(
44+
JwtRegisteredClaimNames.Jti,
45+
Guid.NewGuid()
46+
.ToString()
47+
)
48+
},
49+
expires: DateTime.UtcNow.AddSeconds(_config.LifetimeSeconds),
50+
signingCredentials: signingCredentials
51+
);
52+
53+
var refreshJwt = new JwtSecurityToken(
54+
issuer: issuer,
55+
audience: audience,
56+
notBefore: null,
57+
claims: new[]
58+
{
59+
new Claim(KnownJwtClaims.UserId, userId.ToString()),
60+
// the JTI is used for our refresh token which we will be covering in the next video
61+
new Claim(
62+
JwtRegisteredClaimNames.Jti,
63+
Guid.NewGuid()
64+
.ToString()
65+
)
66+
},
67+
expires: DateTime.UtcNow.AddSeconds(_config.LifetimeSeconds),
68+
signingCredentials: signingCredentials
69+
);
70+
71+
var token = JwtSecurityTokenHandler.WriteToken(tokenJwt);
72+
var refreshToken = JwtSecurityTokenHandler.WriteToken(refreshJwt);
73+
74+
return new(token, refreshToken);
75+
}
76+
}

BusinessLogic/Jwt/JwtInfo.cs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace BusinessLogic.Jwt;
2+
3+
public record JwtInfo(long UserId);

BusinessLogic/KnownJwtClaims.cs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace BusinessLogic;
2+
3+
public class KnownJwtClaims
4+
{
5+
public const string UserId = "user-id";
6+
}

BusinessLogic/Mediatr/LoginUser.cs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using BusinessLogic.Jwt;
2+
using DataAccess.Models;
3+
using DataAccess.RepositoryNew;
4+
using FluentValidation;
5+
using MediatR;
6+
using Microsoft.EntityFrameworkCore;
7+
8+
namespace BusinessLogic.Mediatr;
9+
10+
public static class LoginUser
11+
{
12+
public record Command(string Email, string Password) : IRequest<Response>;
13+
14+
public record Response(string Token, string RefreshToken);
15+
16+
public class CommandValidator : AbstractValidator<Command>
17+
{
18+
public CommandValidator()
19+
{
20+
RuleFor(x => x.Email)
21+
.NotEmpty()
22+
.WithMessage("E-mail не должен быть пустым")
23+
.EmailAddress()
24+
.WithMessage("E-mail должен быть email адресом");
25+
26+
RuleFor(x => x.Password)
27+
.NotEmpty()
28+
.WithMessage("Пароль не должен быть пустым")
29+
.Length(8, 50)
30+
.WithMessage("Длина пароля должна быть от 8 до 50 символов");
31+
}
32+
}
33+
34+
public class Handler : IRequestHandler<Command, Response>
35+
{
36+
private readonly IRepository<AppUser> _repository;
37+
private readonly IJwtGeneratorService _jwtGeneratorService;
38+
39+
public Handler(IRepository<AppUser> repository, IJwtGeneratorService jwtGeneratorService)
40+
{
41+
_repository = repository;
42+
_jwtGeneratorService = jwtGeneratorService;
43+
}
44+
45+
public async Task<Response> Handle(Command request, CancellationToken cancellationToken)
46+
{
47+
var user = await _repository.GetAll()
48+
.FirstOrDefaultAsync(x => x.Email == request.Email, cancellationToken);
49+
50+
if (user is null)
51+
{
52+
throw new BusinessException("Пользователь не найден");
53+
}
54+
55+
if (user.Password != request.Password)
56+
{
57+
throw new BusinessException("Неверный пароль");
58+
}
59+
60+
var jwtGenerationResult = _jwtGeneratorService.GenerateToken(user.Id);
61+
62+
return new Response(jwtGenerationResult.Token, jwtGenerationResult.RefreshToken);
63+
}
64+
}
65+
}

BusinessLogic/Mediatr/RegisterUser.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@ public CommandValidator()
1616
{
1717
RuleFor(x => x.Email)
1818
.NotEmpty()
19-
.EmailAddress();
19+
.WithMessage("E-mail не должен быть пустым")
20+
.EmailAddress()
21+
.WithMessage("E-mail должен быть email адресом");
2022

2123
RuleFor(x => x.Password)
2224
.NotEmpty()
23-
.Length(8, 50);
25+
.WithMessage("Пароль не должен быть пустым")
26+
.Length(8, 50)
27+
.WithMessage("Длина пароля должна быть от 8 до 50 символов");
2428
}
2529
}
2630

BusinessLogic/Registrar.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using FluentValidation;
1+
using BusinessLogic.Jwt;
2+
using FluentValidation;
23
using Microsoft.Extensions.Configuration;
34
using Microsoft.Extensions.DependencyInjection;
45

@@ -14,6 +15,8 @@ public static IServiceCollection AddBLL(this IServiceCollection services, Config
1415

1516
services.AddValidatorsFromAssembly(assembly);
1617

18+
services.AddTransient<IJwtGeneratorService, JwtGeneratorService>();
19+
1720
return services;
1821
}
1922
}

ProjectManager/Controllers/AuthController.cs

+9
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,13 @@ public async Task<ActionResult> Register([FromBody] RegisterUser.Command command
2525

2626
return Ok();
2727
}
28+
29+
[HttpPost]
30+
[ProducesResponseType(typeof(LoginUser.Response), 200)]
31+
public async Task<ActionResult<LoginUser.Response>> Login([FromBody] LoginUser.Command command, CancellationToken cancellationToken)
32+
{
33+
var response = await _mediator.Send(command, cancellationToken);
34+
35+
return Ok(response);
36+
}
2837
}

0 commit comments

Comments
 (0)