新增全局异常捕获

This commit is contained in:
南浔
2024-11-03 00:27:20 +08:00
28 changed files with 1030 additions and 86 deletions
@@ -0,0 +1,41 @@
using Apimanager_backend.Data;
using Apimanager_backend.Dtos;
using Apimanager_backend.Exceptions;
using Apimanager_backend.Models;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
namespace Apimanager_backend.Services
{
public class AuthService:IAuthService
{
private readonly ApiContext apiContext;
private readonly IMapper mapper;
public AuthService(ApiContext apiContext, IMapper automapper)
{
this.apiContext = apiContext;
this.mapper = automapper;
}
public async Task<UserInfoDto> LoginAsync(string username, string password)
{
//查找用户
User? user = await apiContext.Users.Include(x => x.Roles).SingleOrDefaultAsync(x =>
x.Username == username && x.PassHash == password
);
//用户不存在或密码错误都为登录失败
if (user == null)
{
throw new BaseException(2001, "Invalid username or password");
}
//用户被禁用
if (user.IsBan)
{
throw new BaseException(2002, "User account is disabled");
}
return mapper.Map<UserInfoDto>(user);
}
}
}
@@ -0,0 +1,15 @@
using Apimanager_backend.Dtos;
namespace Apimanager_backend.Services
{
public interface IAuthService
{
/// <summary>
/// 登录用户,根据用户名和密码进行身份验证。
/// </summary>
/// <param name="username">用户名</param>
/// <param name="password">密码</param>
/// <returns>包含用户信息的 <see cref="UserInfoBaseDto"/></returns>
Task<UserInfoDto> LoginAsync(string username, string password);
}
}
@@ -0,0 +1,30 @@
namespace Apimanager_backend.Services
{
public interface IRefreshTokenService
{
/// <summary>
/// 创建刷新令牌
/// </summary>
/// <param name="userId">用户id</param>
/// <returns>刷新令牌</returns>
Task<string> CreateRefereshTokenAsync(string userId);
/// <summary>
/// 验证刷新令牌
/// </summary>
/// <param name="refreshToken">刷新令牌</param>
/// <returns>是否验证通过</returns>
Task<string?> ValidateRefreshTokenAsync(string refreshToken);
/// <summary>
/// 删除刷新令牌
/// </summary>
/// <param name="refreshToken">刷新令牌</param>
/// <returns>是否删除成功</returns>
Task DeleterRefreshTokenAsync(string refreshToken);
/// <summary>
/// 更新刷新令牌有效期
/// </summary>
/// <param name="refreshToken">刷新令牌</param>
/// <returns>是否成功</returns>
Task UpdateRefreshTokenAsync(string refreshToken);
}
}
@@ -0,0 +1,16 @@
using Apimanager_backend.Models;
namespace Apimanager_backend.Services
{
public interface ITokenService
{
/// <summary>
/// 拥护凭证
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="username">用户名</param>
/// <param name="role">角色</param>
/// <returns>token</returns>
string GenerateAccessToken(string userId, List<UserRole> roles);
}
}
+3 -9
View File
@@ -6,13 +6,7 @@ namespace Apimanager_backend.Services
{
public interface IUserService
{
/// <summary>
/// 登录用户,根据用户名和密码进行身份验证。
/// </summary>
/// <param name="username">用户名</param>
/// <param name="password">密码</param>
/// <returns>包含用户信息的 <see cref="UserInfoDto"/></returns>
Task<UserInfoDto> LoginAsync(string username, string password);
/// <summary>
/// 发送密码重置邮件到指定邮箱。
@@ -33,9 +27,9 @@ namespace Apimanager_backend.Services
/// <summary>
/// 获取用户信息。
/// </summary>
/// <param name="username">用户</param>
/// <param name="userId">用户ID</param>
/// <returns>包含用户信息的 <see cref="UserInfoDto"/></returns>
Task<UserInfoDto> GetUserAsync(string username);
Task<UserInfoDto> GetUserAsync(int userId);
/// <summary>
/// 更新用户信息。
@@ -0,0 +1,68 @@
using Apimanager_backend.Exceptions;
using StackExchange.Redis;
namespace Apimanager_backend.Services
{
public class RefreshTokenService : IRefreshTokenService
{
private readonly IConnectionMultiplexer redis;
private readonly IConfiguration configuration;
public RefreshTokenService(IConnectionMultiplexer redis, IConfiguration configuration)
{
this.redis = redis;
this.configuration = configuration;
}
public async Task<string> CreateRefereshTokenAsync(string userId)
{
var refreshToken = Guid.NewGuid().ToString();
var expiryDays = Convert.ToDouble(configuration["JwtSettings:RefreshTokenExpiryDays"]);
// 保存到Redis,设置过期时间
var db = redis.GetDatabase();
var res = await db.StringSetAsync(refreshToken, userId, TimeSpan.FromDays(expiryDays));
if (!res)
{
throw new BaseException(1006, "Service unavailable");
}
return refreshToken;
}
public async Task DeleterRefreshTokenAsync(string refreshToken)
{
var db = redis.GetDatabase();
bool res = await db.KeyDeleteAsync(refreshToken);
if (!res)
{
throw new BaseException(1006, "Service unavailable");
}
}
public async Task UpdateRefreshTokenAsync(string refreshToken)
{
var db = redis.GetDatabase();
var expiryDays = Convert.ToDouble(configuration["JwtSettings:RefreshTokenExpiryDays"]);
//获取refresh剩余有效时间
var time =await db.KeyTimeToLiveAsync(refreshToken);
//判断有效时间是否大于零天小于三天,否则不刷新有效期
if(time <= TimeSpan.Zero || time >= TimeSpan.FromDays(3))
{
return;
}
//刷新过期时间
await db.KeyExpireAsync(refreshToken,TimeSpan.FromDays(expiryDays));
}
public async Task<string?> ValidateRefreshTokenAsync(string refreshToken)
{
var db = redis.GetDatabase();
var redisValue = await db.StringGetAsync(refreshToken);
//验证refreshToken是否存在
if (!redisValue.HasValue)
{
return null;
}
return redisValue.ToString();
}
}
}
@@ -0,0 +1,50 @@
using Apimanager_backend.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Apimanager_backend.Services
{
public class TokenService:ITokenService
{
public readonly IConfiguration configuration;
public TokenService(IConfiguration configuration)
{
this.configuration = configuration;
}
public string GenerateAccessToken(string userId,List<UserRole> roles)
{
var jwtSettings = configuration.GetSection("JwtSettings");
// 创建Claims列表,包含用户名和角色信息
var claims = new List<Claim>
{
new Claim("userId", userId), // 使用userId作为唯一标识
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
//添加用户角色
foreach(var role in roles)
{
var claim = new Claim(ClaimTypes.Role, role.Role.ToString());
claims.Add(claim);
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Secret"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: jwtSettings["Issuer"],
audience: jwtSettings["Audience"],
claims: claims,
expires: DateTime.Now.AddMinutes(Convert.ToDouble(jwtSettings["AccessTokenExpiryMinutes"])),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
}
+8 -23
View File
@@ -34,9 +34,15 @@ namespace Apimanager_backend.Services
throw new NotImplementedException();
}
public Task<UserInfoDto> GetUserAsync(string username)
public async Task<UserInfoDto> GetUserAsync(int userId)
{
throw new NotImplementedException();
User? user = await apiContext.Users.SingleOrDefaultAsync(x => x.Id == userId);
//未找到用户
if (user == null)
{
throw new BaseException(2004, "User not found");
}
return mapper.Map<UserInfoDto>(user);
}
public Task<List<UserInfoDto>> GetUsersAsync(int page, int pageSize, bool desc)
@@ -44,27 +50,6 @@ namespace Apimanager_backend.Services
throw new NotImplementedException();
}
public async Task<UserInfoDto> LoginAsync(string username, string password)
{
//查找用户
User? user = await apiContext.Users.SingleOrDefaultAsync(x =>
x.Username == username && x.PassHash == password
);
//用户不存在或密码错误都为登录失败
if(user == null)
{
throw new BaseException(2001, "Invalid username or password");
}
//用户被禁用
if (user.IsBan)
{
throw new BaseException(2002, "User account is disabled");
}
return mapper.Map<UserInfoDto>(user);
}
public Task ResetPasswordAsync(string email, string token, string newPassword)
{