Compare commits
4 Commits
62a859b44c
...
d2ef99d0b2
Author | SHA1 | Date | |
---|---|---|---|
d2ef99d0b2 | |||
38ec80a566 | |||
85802aa514 | |||
f2aa274d0a |
82
Endpoint/Common/Services/Security/JwtTokenService.cs
Normal file
82
Endpoint/Common/Services/Security/JwtTokenService.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Mirea.Api.Security.Common.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Mirea.Api.Endpoint.Common.Services.Security;
|
||||||
|
|
||||||
|
public class JwtTokenService : IAccessToken
|
||||||
|
{
|
||||||
|
public required string Issuer { private get; init; }
|
||||||
|
public required string Audience { private get; init; }
|
||||||
|
public TimeSpan Lifetime { private get; init; }
|
||||||
|
|
||||||
|
public ReadOnlyMemory<byte> EncryptionKey { get; init; }
|
||||||
|
public ReadOnlyMemory<byte> SigningKey { private get; init; }
|
||||||
|
|
||||||
|
public (string Token, DateTime ExpireIn) GenerateToken(string userId)
|
||||||
|
{
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var signingKey = new SymmetricSecurityKey(SigningKey.ToArray());
|
||||||
|
var encryptionKey = new SymmetricSecurityKey(EncryptionKey.ToArray());
|
||||||
|
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha512);
|
||||||
|
|
||||||
|
var expires = DateTime.UtcNow.Add(Lifetime);
|
||||||
|
|
||||||
|
var tokenDescriptor = new SecurityTokenDescriptor
|
||||||
|
{
|
||||||
|
Issuer = Issuer,
|
||||||
|
Audience = Audience,
|
||||||
|
Expires = expires,
|
||||||
|
SigningCredentials = signingCredentials,
|
||||||
|
Subject = new ClaimsIdentity(
|
||||||
|
[
|
||||||
|
new Claim(ClaimTypes.Name, userId),
|
||||||
|
// todo: get role by userId
|
||||||
|
new Claim(ClaimTypes.Role, "")
|
||||||
|
]),
|
||||||
|
EncryptingCredentials = new EncryptingCredentials(encryptionKey, SecurityAlgorithms.Aes256KW, SecurityAlgorithms.Aes256CbcHmacSha512)
|
||||||
|
};
|
||||||
|
|
||||||
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||||
|
|
||||||
|
return (tokenHandler.WriteToken(token), expires);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTimeOffset GetExpireDateTime(string token)
|
||||||
|
{
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var signingKey = new SymmetricSecurityKey(SigningKey.ToArray());
|
||||||
|
var encryptionKey = new SymmetricSecurityKey(EncryptionKey.ToArray());
|
||||||
|
|
||||||
|
var tokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidIssuer = Issuer,
|
||||||
|
ValidAudience = Audience,
|
||||||
|
IssuerSigningKey = signingKey,
|
||||||
|
TokenDecryptionKey = encryptionKey,
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
ValidateLifetime = false
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var claimsPrincipal = tokenHandler.ValidateToken(token, tokenValidationParameters, out _);
|
||||||
|
|
||||||
|
var expClaim = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == "exp");
|
||||||
|
|
||||||
|
if (expClaim != null && long.TryParse(expClaim.Value, out var expUnix))
|
||||||
|
return DateTimeOffset.FromUnixTimeSeconds(expUnix);
|
||||||
|
}
|
||||||
|
catch (SecurityTokenException)
|
||||||
|
{
|
||||||
|
return DateTimeOffset.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateTimeOffset.MinValue;
|
||||||
|
}
|
||||||
|
}
|
@ -23,9 +23,10 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Cronos" Version="0.8.4" />
|
<PackageReference Include="Cronos" Version="0.8.4" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.7.33" />
|
<PackageReference Include="StackExchange.Redis" Version="2.7.33" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Versioning" Version="2.0.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Versioning" Version="2.0.0" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||||
@ -6,20 +7,24 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Mirea.Api.DataAccess.Application;
|
using Mirea.Api.DataAccess.Application;
|
||||||
using Mirea.Api.DataAccess.Persistence;
|
using Mirea.Api.DataAccess.Persistence;
|
||||||
using Mirea.Api.Endpoint.Common.Interfaces;
|
using Mirea.Api.Endpoint.Common.Interfaces;
|
||||||
using Mirea.Api.Endpoint.Common.Services;
|
using Mirea.Api.Endpoint.Common.Services;
|
||||||
|
using Mirea.Api.Endpoint.Common.Services.Security;
|
||||||
using Mirea.Api.Endpoint.Configuration;
|
using Mirea.Api.Endpoint.Configuration;
|
||||||
using Mirea.Api.Endpoint.Configuration.General;
|
using Mirea.Api.Endpoint.Configuration.General;
|
||||||
using Mirea.Api.Endpoint.Configuration.General.Validators;
|
using Mirea.Api.Endpoint.Configuration.General.Validators;
|
||||||
using Mirea.Api.Endpoint.Configuration.Swagger;
|
using Mirea.Api.Endpoint.Configuration.Swagger;
|
||||||
using Mirea.Api.Endpoint.Middleware;
|
using Mirea.Api.Endpoint.Middleware;
|
||||||
|
using Mirea.Api.Security.Common.Interfaces;
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Mirea.Api.Endpoint;
|
namespace Mirea.Api.Endpoint;
|
||||||
|
|
||||||
@ -40,6 +45,66 @@ public class Program
|
|||||||
return result.Build();
|
return result.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IServiceCollection ConfigureJwtToken(IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var lifeTimeJwt = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_JWT"]!));
|
||||||
|
|
||||||
|
var jwtDecrypt = Encoding.UTF8.GetBytes(configuration["SECURITY_ENCRYPTION_TOKEN"] ?? string.Empty);
|
||||||
|
|
||||||
|
if (jwtDecrypt.Length != 32)
|
||||||
|
throw new InvalidOperationException("The secret token \"SECURITY_ENCRYPTION_TOKEN\" cannot be less than 32 characters long. Now the size is equal is " + jwtDecrypt.Length);
|
||||||
|
|
||||||
|
var jwtKey = Encoding.UTF8.GetBytes(configuration["SECURITY_SIGNING_TOKEN"] ?? string.Empty);
|
||||||
|
|
||||||
|
if (jwtKey.Length != 64)
|
||||||
|
throw new InvalidOperationException("The signature token \"SECURITY_SIGNING_TOKEN\" cannot be less than 64 characters. Now the size is " + jwtKey.Length);
|
||||||
|
|
||||||
|
var jwtIssuer = configuration["SECURITY_JWT_ISSUER"];
|
||||||
|
var jwtAudience = configuration["SECURITY_JWT_AUDIENCE"];
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(jwtAudience) || string.IsNullOrEmpty(jwtIssuer))
|
||||||
|
throw new InvalidOperationException("The \"SECURITY_JWT_ISSUER\" and \"SECURITY_JWT_AUDIENCE\" are not specified");
|
||||||
|
|
||||||
|
services.AddSingleton<IAccessToken, JwtTokenService>(_ => new JwtTokenService
|
||||||
|
{
|
||||||
|
Audience = jwtAudience,
|
||||||
|
Issuer = jwtIssuer,
|
||||||
|
Lifetime = lifeTimeJwt,
|
||||||
|
EncryptionKey = jwtDecrypt,
|
||||||
|
SigningKey = jwtKey
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
}).AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidIssuer = jwtIssuer,
|
||||||
|
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidAudience = jwtAudience,
|
||||||
|
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(jwtKey),
|
||||||
|
TokenDecryptionKey = new SymmetricSecurityKey(jwtDecrypt)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IServiceCollection ConfigureSecurity(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<IAccessToken, JwtTokenService>();
|
||||||
|
services.AddSingleton<IRevokedToken, MemoryRevokedTokenService>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
|
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
|
||||||
|
Loading…
Reference in New Issue
Block a user