12 changed files with 144 additions and 145 deletions
@ -0,0 +1,134 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using CellularManagement.Domain.Options; |
|||
using CellularManagement.Domain.Services; |
|||
|
|||
namespace CellularManagement.Infrastructure.Services.Authentication; |
|||
|
|||
/// <summary>
|
|||
/// JWT 验证服务实现
|
|||
/// </summary>
|
|||
public class JwtValidationService : IJwtValidationService |
|||
{ |
|||
/// <inheritdoc />
|
|||
public void ValidateOptions(JwtOptions options) |
|||
{ |
|||
if (string.IsNullOrEmpty(options.SecretKey)) |
|||
{ |
|||
throw new ArgumentException("JWT密钥不能为空"); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(options.Issuer)) |
|||
{ |
|||
throw new ArgumentException("JWT颁发者不能为空"); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(options.Audience)) |
|||
{ |
|||
throw new ArgumentException("JWT受众不能为空"); |
|||
} |
|||
|
|||
if (options.ExpiryMinutes <= 0) |
|||
{ |
|||
throw new ArgumentException("JWT过期时间必须大于0"); |
|||
} |
|||
|
|||
if (options.RefreshTokenExpiryDays <= 0) |
|||
{ |
|||
throw new ArgumentException("刷新令牌过期时间必须大于0"); |
|||
} |
|||
|
|||
if (options.ClockSkewMinutes < 0) |
|||
{ |
|||
throw new ArgumentException("时钟偏差不能为负数"); |
|||
} |
|||
|
|||
if (options.KeyRotationDays <= 0) |
|||
{ |
|||
throw new ArgumentException("密钥轮换间隔必须大于0"); |
|||
} |
|||
|
|||
if (options.MinKeyLength < 32) |
|||
{ |
|||
throw new ArgumentException("密钥最小长度必须至少为32字节"); |
|||
} |
|||
|
|||
// 验证密钥是否为有效的Base64字符串
|
|||
try |
|||
{ |
|||
var keyBytes = Convert.FromBase64String(options.SecretKey); |
|||
if (keyBytes.Length < options.MinKeyLength) |
|||
{ |
|||
throw new ArgumentException($"密钥长度必须至少为{options.MinKeyLength}字节"); |
|||
} |
|||
} |
|||
catch (FormatException) |
|||
{ |
|||
throw new ArgumentException("JWT密钥不是有效的Base64字符串"); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
/// <inheritdoc />
|
|||
public void ValidateKeyStrength(string secretKey, int minKeyLength) |
|||
{ |
|||
if (string.IsNullOrEmpty(secretKey)) |
|||
{ |
|||
throw new ArgumentException("密钥不能为空"); |
|||
} |
|||
|
|||
// 验证密钥是否为有效的Base64字符串
|
|||
try |
|||
{ |
|||
var keyBytes = Convert.FromBase64String(secretKey); |
|||
if (keyBytes.Length < minKeyLength) |
|||
{ |
|||
throw new ArgumentException($"密钥长度必须至少为 {minKeyLength} 字节"); |
|||
} |
|||
} |
|||
catch (FormatException) |
|||
{ |
|||
throw new ArgumentException("密钥不是有效的Base64字符串"); |
|||
} |
|||
|
|||
// 检查密钥是否包含足够的随机性
|
|||
var entropy = CalculateEntropy(secretKey); |
|||
if (entropy < 3.5) // 3.5 bits per character is considered good
|
|||
{ |
|||
throw new ArgumentException("密钥随机性不足"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 计算字符串熵值
|
|||
/// </summary>
|
|||
private static double CalculateEntropy(string input) |
|||
{ |
|||
var charCounts = new Dictionary<char, int>(); |
|||
foreach (var c in input) |
|||
{ |
|||
if (charCounts.ContainsKey(c)) |
|||
{ |
|||
charCounts[c]++; |
|||
} |
|||
else |
|||
{ |
|||
charCounts[c] = 1; |
|||
} |
|||
} |
|||
|
|||
var length = input.Length; |
|||
var entropy = 0.0; |
|||
foreach (var count in charCounts.Values) |
|||
{ |
|||
var probability = (double)count / length; |
|||
entropy -= probability * Math.Log2(probability); |
|||
} |
|||
|
|||
return entropy; |
|||
} |
|||
} |
@ -1,7 +1,7 @@ |
|||
using CellularManagement.Domain.Services; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
|
|||
namespace CellularManagement.Infrastructure.Services; |
|||
namespace CellularManagement.Infrastructure.Services.Infrastructure; |
|||
|
|||
/// <summary>
|
|||
/// 内存缓存服务实现
|
@ -1,135 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using CellularManagement.Domain.Options; |
|||
using CellularManagement.Domain.Services; |
|||
|
|||
namespace CellularManagement.Infrastructure.Services |
|||
{ |
|||
/// <summary>
|
|||
/// JWT 验证服务实现
|
|||
/// </summary>
|
|||
public class JwtValidationService : IJwtValidationService |
|||
{ |
|||
/// <inheritdoc />
|
|||
public void ValidateOptions(JwtOptions options) |
|||
{ |
|||
if (string.IsNullOrEmpty(options.SecretKey)) |
|||
{ |
|||
throw new ArgumentException("JWT密钥不能为空"); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(options.Issuer)) |
|||
{ |
|||
throw new ArgumentException("JWT颁发者不能为空"); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(options.Audience)) |
|||
{ |
|||
throw new ArgumentException("JWT受众不能为空"); |
|||
} |
|||
|
|||
if (options.ExpiryMinutes <= 0) |
|||
{ |
|||
throw new ArgumentException("JWT过期时间必须大于0"); |
|||
} |
|||
|
|||
if (options.RefreshTokenExpiryDays <= 0) |
|||
{ |
|||
throw new ArgumentException("刷新令牌过期时间必须大于0"); |
|||
} |
|||
|
|||
if (options.ClockSkewMinutes < 0) |
|||
{ |
|||
throw new ArgumentException("时钟偏差不能为负数"); |
|||
} |
|||
|
|||
if (options.KeyRotationDays <= 0) |
|||
{ |
|||
throw new ArgumentException("密钥轮换间隔必须大于0"); |
|||
} |
|||
|
|||
if (options.MinKeyLength < 32) |
|||
{ |
|||
throw new ArgumentException("密钥最小长度必须至少为32字节"); |
|||
} |
|||
|
|||
// 验证密钥是否为有效的Base64字符串
|
|||
try |
|||
{ |
|||
var keyBytes = Convert.FromBase64String(options.SecretKey); |
|||
if (keyBytes.Length < options.MinKeyLength) |
|||
{ |
|||
throw new ArgumentException($"密钥长度必须至少为{options.MinKeyLength}字节"); |
|||
} |
|||
} |
|||
catch (FormatException) |
|||
{ |
|||
throw new ArgumentException("JWT密钥不是有效的Base64字符串"); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
/// <inheritdoc />
|
|||
public void ValidateKeyStrength(string secretKey, int minKeyLength) |
|||
{ |
|||
if (string.IsNullOrEmpty(secretKey)) |
|||
{ |
|||
throw new ArgumentException("密钥不能为空"); |
|||
} |
|||
|
|||
// 验证密钥是否为有效的Base64字符串
|
|||
try |
|||
{ |
|||
var keyBytes = Convert.FromBase64String(secretKey); |
|||
if (keyBytes.Length < minKeyLength) |
|||
{ |
|||
throw new ArgumentException($"密钥长度必须至少为 {minKeyLength} 字节"); |
|||
} |
|||
} |
|||
catch (FormatException) |
|||
{ |
|||
throw new ArgumentException("密钥不是有效的Base64字符串"); |
|||
} |
|||
|
|||
// 检查密钥是否包含足够的随机性
|
|||
var entropy = CalculateEntropy(secretKey); |
|||
if (entropy < 3.5) // 3.5 bits per character is considered good
|
|||
{ |
|||
throw new ArgumentException("密钥随机性不足"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 计算字符串熵值
|
|||
/// </summary>
|
|||
private static double CalculateEntropy(string input) |
|||
{ |
|||
var charCounts = new Dictionary<char, int>(); |
|||
foreach (var c in input) |
|||
{ |
|||
if (charCounts.ContainsKey(c)) |
|||
{ |
|||
charCounts[c]++; |
|||
} |
|||
else |
|||
{ |
|||
charCounts[c] = 1; |
|||
} |
|||
} |
|||
|
|||
var length = input.Length; |
|||
var entropy = 0.0; |
|||
foreach (var count in charCounts.Values) |
|||
{ |
|||
var probability = (double)count / length; |
|||
entropy -= probability * Math.Log2(probability); |
|||
} |
|||
|
|||
return entropy; |
|||
} |
|||
} |
|||
} |
@ -1,7 +1,7 @@ |
|||
using CellularManagement.Domain.Services; |
|||
using SkiaSharp; |
|||
|
|||
namespace CellularManagement.Infrastructure.Services; |
|||
namespace CellularManagement.Infrastructure.Services.Security; |
|||
|
|||
public class CaptchaService : ICaptchaService |
|||
{ |
Loading…
Reference in new issue