57 changed files with 496 additions and 13 deletions
@ -1,9 +1,10 @@ |
|||||
using Microsoft.Extensions.Caching.Memory; |
using Microsoft.Extensions.Caching.Memory; |
||||
|
|
||||
namespace CellularManagement.Application.Services; |
namespace CellularManagement.Domain.Services; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 缓存服务接口
|
/// 缓存服务接口
|
||||
|
/// 定义缓存操作的核心业务逻辑
|
||||
/// </summary>
|
/// </summary>
|
||||
public interface ICacheService |
public interface ICacheService |
||||
{ |
{ |
@ -0,0 +1,33 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CellularManagement.Domain.Services; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 邮箱服务接口
|
||||
|
/// 定义发送邮件和验证邮箱的基本功能
|
||||
|
/// </summary>
|
||||
|
public interface IEmailService |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 发送验证码邮件
|
||||
|
/// </summary>
|
||||
|
/// <param name="to">收件人邮箱</param>
|
||||
|
/// <param name="verificationCode">验证码</param>
|
||||
|
/// <returns>发送结果</returns>
|
||||
|
Task<bool> SendVerificationCodeAsync(string to, string verificationCode); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 发送注册成功邮件
|
||||
|
/// </summary>
|
||||
|
/// <param name="to">收件人邮箱</param>
|
||||
|
/// <param name="username">用户名</param>
|
||||
|
/// <returns>发送结果</returns>
|
||||
|
Task<bool> SendRegistrationSuccessAsync(string to, string username); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 验证邮箱格式
|
||||
|
/// </summary>
|
||||
|
/// <param name="email">邮箱地址</param>
|
||||
|
/// <returns>验证结果</returns>
|
||||
|
bool ValidateEmail(string email); |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CellularManagement.Domain.Services; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 邮箱验证码服务接口
|
||||
|
/// 定义验证码生成、发送和验证的核心业务逻辑
|
||||
|
/// </summary>
|
||||
|
public interface IEmailVerificationService |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 生成并发送验证码
|
||||
|
/// </summary>
|
||||
|
/// <param name="email">邮箱地址</param>
|
||||
|
/// <returns>是否发送成功</returns>
|
||||
|
Task<bool> GenerateAndSendVerificationCodeAsync(string email); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 验证验证码
|
||||
|
/// </summary>
|
||||
|
/// <param name="email">邮箱地址</param>
|
||||
|
/// <param name="code">验证码</param>
|
||||
|
/// <returns>验证结果</returns>
|
||||
|
Task<bool> VerifyCodeAsync(string email, string code); |
||||
|
} |
@ -1,6 +1,7 @@ |
|||||
|
using System.Collections.Generic; |
||||
using System.Security.Claims; |
using System.Security.Claims; |
||||
|
|
||||
namespace CellularManagement.Application.Services; |
namespace CellularManagement.Domain.Services; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// JWT 令牌提供者接口
|
/// JWT 令牌提供者接口
|
@ -1,9 +1,10 @@ |
|||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
|
|
||||
namespace CellularManagement.Application.Services; |
namespace CellularManagement.Domain.Services; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 密钥轮换服务接口
|
/// 密钥轮换服务接口
|
||||
|
/// 定义密钥轮换的核心业务逻辑
|
||||
/// </summary>
|
/// </summary>
|
||||
public interface IKeyRotationService |
public interface IKeyRotationService |
||||
{ |
{ |
@ -0,0 +1,42 @@ |
|||||
|
namespace CellularManagement.Infrastructure.Options; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 邮箱配置选项
|
||||
|
/// </summary>
|
||||
|
public class EmailOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 配置节点名称
|
||||
|
/// </summary>
|
||||
|
public const string SectionName = "Email"; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SMTP服务器地址
|
||||
|
/// </summary>
|
||||
|
public string SmtpServer { get; set; } = string.Empty; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SMTP服务器端口
|
||||
|
/// </summary>
|
||||
|
public int SmtpPort { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 发件人邮箱地址
|
||||
|
/// </summary>
|
||||
|
public string FromEmail { get; set; } = string.Empty; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 发件人显示名称
|
||||
|
/// </summary>
|
||||
|
public string FromName { get; set; } = string.Empty; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 邮箱密码或授权码
|
||||
|
/// </summary>
|
||||
|
public string Password { get; set; } = string.Empty; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 是否启用SSL
|
||||
|
/// </summary>
|
||||
|
public bool EnableSsl { get; set; } |
||||
|
} |
@ -0,0 +1,116 @@ |
|||||
|
using System; |
||||
|
using System.Text.RegularExpressions; |
||||
|
using System.Threading.Tasks; |
||||
|
using CellularManagement.Domain.Services; |
||||
|
using CellularManagement.Infrastructure.Options; |
||||
|
using MailKit.Net.Smtp; |
||||
|
using MailKit.Security; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using MimeKit; |
||||
|
|
||||
|
namespace CellularManagement.Infrastructure.Services; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 邮箱服务实现类
|
||||
|
/// </summary>
|
||||
|
public class EmailService : IEmailService |
||||
|
{ |
||||
|
private readonly EmailOptions _emailOptions; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 构造函数
|
||||
|
/// </summary>
|
||||
|
/// <param name="emailOptions">邮箱配置选项</param>
|
||||
|
public EmailService(IOptions<EmailOptions> emailOptions) |
||||
|
{ |
||||
|
_emailOptions = emailOptions.Value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 发送验证码邮件
|
||||
|
/// </summary>
|
||||
|
public async Task<bool> SendVerificationCodeAsync(string to, string verificationCode) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var message = new MimeMessage(); |
||||
|
message.From.Add(new MailboxAddress(_emailOptions.FromName, _emailOptions.FromEmail)); |
||||
|
message.To.Add(new MailboxAddress("", to)); |
||||
|
message.Subject = "邮箱验证码"; |
||||
|
|
||||
|
var bodyBuilder = new BodyBuilder |
||||
|
{ |
||||
|
HtmlBody = $@"
|
||||
|
<h2>您的验证码</h2> |
||||
|
<p>您的验证码是:<strong>{verificationCode}</strong></p> |
||||
|
<p>验证码有效期为5分钟,请尽快使用。</p> |
||||
|
<p>如果这不是您的操作,请忽略此邮件。</p>"
|
||||
|
}; |
||||
|
|
||||
|
message.Body = bodyBuilder.ToMessageBody(); |
||||
|
|
||||
|
using var client = new SmtpClient(); |
||||
|
await client.ConnectAsync(_emailOptions.SmtpServer, _emailOptions.SmtpPort, _emailOptions.EnableSsl ? SecureSocketOptions.SslOnConnect : SecureSocketOptions.StartTls); |
||||
|
await client.AuthenticateAsync(_emailOptions.FromEmail, _emailOptions.Password); |
||||
|
await client.SendAsync(message); |
||||
|
await client.DisconnectAsync(true); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
catch (Exception) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 发送注册成功邮件
|
||||
|
/// </summary>
|
||||
|
public async Task<bool> SendRegistrationSuccessAsync(string to, string username) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var message = new MimeMessage(); |
||||
|
message.From.Add(new MailboxAddress(_emailOptions.FromName, _emailOptions.FromEmail)); |
||||
|
message.To.Add(new MailboxAddress(username, to)); |
||||
|
message.Subject = "注册成功通知"; |
||||
|
|
||||
|
var bodyBuilder = new BodyBuilder |
||||
|
{ |
||||
|
HtmlBody = $@"
|
||||
|
<h2>注册成功</h2> |
||||
|
<p>尊敬的 {username}:</p> |
||||
|
<p>恭喜您成功注册我们的服务!</p> |
||||
|
<p>您现在可以使用注册的邮箱和密码登录系统。</p> |
||||
|
<p>如有任何问题,请随时联系我们。</p>"
|
||||
|
}; |
||||
|
|
||||
|
message.Body = bodyBuilder.ToMessageBody(); |
||||
|
|
||||
|
using var client = new SmtpClient(); |
||||
|
await client.ConnectAsync(_emailOptions.SmtpServer, _emailOptions.SmtpPort, _emailOptions.EnableSsl ? SecureSocketOptions.SslOnConnect : SecureSocketOptions.StartTls); |
||||
|
await client.AuthenticateAsync(_emailOptions.FromEmail, _emailOptions.Password); |
||||
|
await client.SendAsync(message); |
||||
|
await client.DisconnectAsync(true); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
catch (Exception) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 验证邮箱格式
|
||||
|
/// </summary>
|
||||
|
public bool ValidateEmail(string email) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(email)) |
||||
|
return false; |
||||
|
|
||||
|
// 使用正则表达式验证邮箱格式
|
||||
|
const string pattern = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"; |
||||
|
return Regex.IsMatch(email, pattern); |
||||
|
} |
||||
|
} |
@ -0,0 +1,132 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using CellularManagement.Domain.Services; |
||||
|
using Microsoft.Extensions.Caching.Distributed; |
||||
|
using System.Text.Json; |
||||
|
|
||||
|
namespace CellularManagement.Infrastructure.Services; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 邮箱验证码服务
|
||||
|
/// 提供验证码生成、发送和验证的具体实现
|
||||
|
/// </summary>
|
||||
|
public class EmailVerificationService : IEmailVerificationService |
||||
|
{ |
||||
|
private readonly IEmailService _emailService; |
||||
|
private readonly IDistributedCache _cache; |
||||
|
private const string CacheKeyPrefix = "EmailVerification_"; |
||||
|
private const int VerificationCodeLength = 6; |
||||
|
private const int VerificationCodeExpirationMinutes = 5; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 构造函数
|
||||
|
/// </summary>
|
||||
|
/// <param name="emailService">邮箱服务</param>
|
||||
|
/// <param name="cache">分布式缓存</param>
|
||||
|
public EmailVerificationService(IEmailService emailService, IDistributedCache cache) |
||||
|
{ |
||||
|
_emailService = emailService; |
||||
|
_cache = cache; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 生成并发送验证码
|
||||
|
/// </summary>
|
||||
|
/// <param name="email">邮箱地址</param>
|
||||
|
/// <returns>是否发送成功</returns>
|
||||
|
public async Task<bool> GenerateAndSendVerificationCodeAsync(string email) |
||||
|
{ |
||||
|
if (!_emailService.ValidateEmail(email)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 生成6位数字验证码
|
||||
|
var verificationCode = GenerateVerificationCode(); |
||||
|
|
||||
|
// 将验证码保存到缓存
|
||||
|
var cacheKey = $"{CacheKeyPrefix}{email}"; |
||||
|
var cacheValue = JsonSerializer.Serialize(new |
||||
|
{ |
||||
|
Code = verificationCode, |
||||
|
CreatedAt = DateTime.UtcNow |
||||
|
}); |
||||
|
|
||||
|
await _cache.SetStringAsync( |
||||
|
cacheKey, |
||||
|
cacheValue, |
||||
|
new DistributedCacheEntryOptions |
||||
|
{ |
||||
|
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(VerificationCodeExpirationMinutes) |
||||
|
}); |
||||
|
|
||||
|
// 发送验证码邮件
|
||||
|
return await _emailService.SendVerificationCodeAsync(email, verificationCode); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 验证验证码
|
||||
|
/// </summary>
|
||||
|
/// <param name="email">邮箱地址</param>
|
||||
|
/// <param name="code">验证码</param>
|
||||
|
/// <returns>验证结果</returns>
|
||||
|
public async Task<bool> VerifyCodeAsync(string email, string code) |
||||
|
{ |
||||
|
var cacheKey = $"{CacheKeyPrefix}{email}"; |
||||
|
var cachedValue = await _cache.GetStringAsync(cacheKey); |
||||
|
|
||||
|
if (string.IsNullOrEmpty(cachedValue)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var verificationData = JsonSerializer.Deserialize<VerificationData>(cachedValue); |
||||
|
|
||||
|
if (verificationData == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 验证码是否过期
|
||||
|
if (DateTime.UtcNow - verificationData.CreatedAt > TimeSpan.FromMinutes(VerificationCodeExpirationMinutes)) |
||||
|
{ |
||||
|
await _cache.RemoveAsync(cacheKey); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 验证码是否匹配
|
||||
|
if (verificationData.Code != code) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 验证成功后删除缓存
|
||||
|
await _cache.RemoveAsync(cacheKey); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 生成验证码
|
||||
|
/// </summary>
|
||||
|
private string GenerateVerificationCode() |
||||
|
{ |
||||
|
var random = new Random(); |
||||
|
var code = string.Empty; |
||||
|
|
||||
|
for (int i = 0; i < VerificationCodeLength; i++) |
||||
|
{ |
||||
|
code += random.Next(0, 10).ToString(); |
||||
|
} |
||||
|
|
||||
|
return code; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 验证数据
|
||||
|
/// </summary>
|
||||
|
private class VerificationData |
||||
|
{ |
||||
|
public string Code { get; set; } = string.Empty; |
||||
|
public DateTime CreatedAt { get; set; } |
||||
|
} |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Information", |
||||
|
"Microsoft.AspNetCore": "Warning" |
||||
|
} |
||||
|
}, |
||||
|
"AllowedHosts": "*", |
||||
|
"Email": { |
||||
|
"SmtpServer": "smtp.example.com", |
||||
|
"SmtpPort": 587, |
||||
|
"FromEmail": "noreply@example.com", |
||||
|
"FromName": "系统通知", |
||||
|
"Password": "your-email-password", |
||||
|
"EnableSsl": true |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue