208 lines
8.8 KiB
C#
208 lines
8.8 KiB
C#
using Microsoft.AspNetCore.Http.HttpResults;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using SimpleTodoApiWithPg.Data;
|
|
using SimpleTodoApiWithPg.Models;
|
|
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
|
namespace SimpleTodoApiWithPg.UserService
|
|
{
|
|
// Services/UserService.cs
|
|
public class UserServices
|
|
{
|
|
private readonly AppDbContext _dbContext;
|
|
private readonly ILogger<UserServices> _logger; // 在服务构造函数中注入
|
|
private readonly IConfiguration _configuration;
|
|
|
|
public UserServices(AppDbContext dbContext, IConfiguration configuration, ILogger<UserServices> logger)
|
|
{
|
|
_dbContext = dbContext;
|
|
_logger = logger;
|
|
_configuration = configuration;
|
|
}
|
|
|
|
|
|
//前更新用户接口,暂时未使用
|
|
public async Task<Results<Ok<User>, BadRequest<string>>> PatchUserAsync(User user)
|
|
{
|
|
_logger.LogInformation("Attempting to patch user in UserService: {Username}", user.Username);
|
|
|
|
var existingUser = await _dbContext.Users.FirstOrDefaultAsync(u => u.Username == user.Username);
|
|
if (existingUser == null)
|
|
{
|
|
_logger.LogWarning("User '{Username}' not found during patch.", user.Username);
|
|
return TypedResults.BadRequest("用户名不存在,登录失败!。");
|
|
}
|
|
|
|
if (!BCrypt.Net.BCrypt.Verify(user.PasswordHash, existingUser.PasswordHash, true))
|
|
{
|
|
_logger.LogWarning("Incorrect password for user '{Username}' during patch.", user.Username);
|
|
return TypedResults.BadRequest("密码错误,请重新输入。");
|
|
}
|
|
|
|
// 假设这里执行更新逻辑
|
|
// existingUser.SomeProperty = user.SomeProperty;
|
|
// await _dbContext.SaveChangesAsync();
|
|
|
|
_logger.LogInformation("User '{Username}' patched successfully.", user.Username);
|
|
return TypedResults.Ok(existingUser);
|
|
}
|
|
|
|
//获取所有用户接口
|
|
public async Task<Results<Ok<List<User>>, BadRequest<string>>> GetUsersAsync()
|
|
{
|
|
var users = await _dbContext.Users.ToListAsync();
|
|
return TypedResults.Ok(users);
|
|
}
|
|
|
|
//根据id获取对应用户接口
|
|
public async Task<Results<Ok<User>, NotFound<string>>> GetoneUserAsync(Guid id)
|
|
{
|
|
var user = await _dbContext.Users.FindAsync(id);
|
|
if (user == null) { return TypedResults.NotFound($"要查询的id:{id}不存在"); }
|
|
return TypedResults.Ok(user);
|
|
}
|
|
|
|
//用户注册接口
|
|
public async Task<ApiResponse<RegisterResponse>> RegisterAsync(RegisterRequest request)
|
|
{
|
|
var exuser = await _dbContext.Users.AnyAsync(u => u.Username == request.Username);
|
|
if (exuser) { return ApiResponse.Fail<RegisterResponse>($"用户名:{request.Username}已存在,注册失败!", 400); };
|
|
//if (request.Username.Length <5 || request.Email.Length < 5 ) { return ApiResponse.Fail<RegisterResponse>($"用户名或邮箱不符合规范,注册失败!", 400);};
|
|
|
|
var password = RandomNumberGenerator.GetString("0123456789",15);
|
|
var user = new User();
|
|
user.Username = request.Username;
|
|
user.Email = request.Email;
|
|
user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(password, 10, true);
|
|
_dbContext.Users.Add(user);
|
|
await _dbContext.SaveChangesAsync();
|
|
return ApiResponse.Ok<RegisterResponse>(new RegisterResponse
|
|
{
|
|
Username = user.Username,
|
|
Email = user.Email,
|
|
Password = password,
|
|
UserId = user.Id
|
|
});
|
|
}
|
|
|
|
//更新用户接口
|
|
public async Task<Results<Ok<User>, NotFound<string>>> UpdateUserAsync(int id, UpdateUserRequest updateUser)
|
|
{
|
|
var existingUser = await _dbContext.Users.FindAsync(id);
|
|
if (existingUser == null) { return TypedResults.NotFound($"要更新的用户id:{id}不存在!"); };
|
|
|
|
if (updateUser.Username != null)
|
|
{
|
|
existingUser.Username = updateUser.Username;
|
|
}
|
|
if (!string.IsNullOrEmpty(updateUser.NewPassword))
|
|
{
|
|
existingUser.PasswordHash = BCrypt.Net.BCrypt.HashPassword(updateUser.NewPassword,10,true);
|
|
}
|
|
if (updateUser.Email != null)
|
|
{
|
|
existingUser.Email = updateUser.Email;
|
|
}
|
|
|
|
await _dbContext.SaveChangesAsync();
|
|
|
|
return TypedResults.Ok(existingUser);
|
|
}
|
|
|
|
//删除指定用户接口
|
|
public async Task<Results<Ok<string>, NotFound<string>>> DeleteUserAsync(Guid id)
|
|
{
|
|
var existingUser = await _dbContext.Users.FindAsync(id);
|
|
if (existingUser == null)
|
|
{
|
|
return TypedResults.NotFound($"指定要删除的用户:{id}不存在!");
|
|
}
|
|
_dbContext.Users.Remove(existingUser);
|
|
await _dbContext.SaveChangesAsync();
|
|
return TypedResults.Ok($"指定的用户:{existingUser.Username}删除成功!");
|
|
}
|
|
|
|
//登录认证接口
|
|
public async Task<ApiResponse<AuthResponse>> LoginAuthAsync(LoginRequest loginRequest)
|
|
{
|
|
var existingUser = await _dbContext.Users.FirstOrDefaultAsync(u => u.Username == loginRequest.Username);
|
|
if (existingUser == null || !BCrypt.Net.BCrypt.Verify(loginRequest.Password, existingUser.PasswordHash, true))
|
|
{
|
|
_logger.LogError("用户登录失败!{request}"," ---用户名: "+loginRequest.Username+" ---密码: "+loginRequest.Password);
|
|
return ApiResponse.Fail<AuthResponse>("用户名或密码错误!",400);
|
|
}
|
|
// 1. 生成 JWT (Access Token)
|
|
var accessTokenExpiration = DateTime.UtcNow.AddMinutes(1); // 访问令牌有效期15分钟
|
|
var accessToken = GenerateJwtToken(existingUser, accessTokenExpiration);
|
|
|
|
// 2. 生成刷新令牌
|
|
var refreshToken = GenerateRefreshToken();
|
|
refreshToken.UserId = existingUser.Id;
|
|
await _dbContext.RefreshTokens.AddAsync(refreshToken);
|
|
|
|
// (可选但推荐) 清理该用户旧的、无效的刷新令牌
|
|
var oldRefreshTokens = await _dbContext.RefreshTokens
|
|
.Where(rt => rt.UserId == existingUser.Id && (rt.Revoked != null || rt.Expires <= DateTime.UtcNow))
|
|
.ToListAsync();
|
|
_dbContext.RefreshTokens.RemoveRange(oldRefreshTokens);
|
|
|
|
await _dbContext.SaveChangesAsync();
|
|
|
|
// 3. 返回令牌对
|
|
return ApiResponse.Ok(new AuthResponse
|
|
{
|
|
AccessToken = accessToken,
|
|
RefreshToken = refreshToken.Token,
|
|
AccessTokenExpiration = accessTokenExpiration
|
|
});
|
|
}
|
|
|
|
|
|
//创建令牌接口
|
|
public string GenerateJwtToken(User user, DateTime expires)
|
|
{
|
|
// 加载证书以获取私钥进行签名
|
|
var certPath = _configuration["Jwt:CertificatePath"]!;
|
|
var certPassword = _configuration["Jwt:CertificatePassword"]!;
|
|
var certificate = X509CertificateLoader.LoadPkcs12FromFile(certPath, certPassword,X509KeyStorageFlags.EphemeralKeySet);
|
|
|
|
var claims = new[]
|
|
{
|
|
new Claim(JwtRegisteredClaimNames.Sub, user.Username),
|
|
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
|
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())
|
|
};
|
|
|
|
var token = new JwtSecurityToken(
|
|
issuer: _configuration["Jwt:Issuer"],
|
|
audience: _configuration["Jwt:Audience"],
|
|
claims: claims,
|
|
expires: expires,
|
|
// 使用证书的私钥通过 ECDSA-SHA256 算法进行签名
|
|
signingCredentials: new SigningCredentials(new ECDsaSecurityKey(certificate.GetECDsaPrivateKey()!), SecurityAlgorithms.EcdsaSha256)
|
|
);
|
|
|
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
|
}
|
|
|
|
//创建刷新令牌接口
|
|
public RefreshToken GenerateRefreshToken()
|
|
{
|
|
var randomNumber = new byte[64];
|
|
using var rng = RandomNumberGenerator.Create();
|
|
rng.GetBytes(randomNumber);
|
|
|
|
return new RefreshToken
|
|
{
|
|
Token = Convert.ToBase64String(randomNumber),
|
|
Expires = DateTime.UtcNow.AddDays(1), // 刷新令牌有效期7天
|
|
Created = DateTime.UtcNow
|
|
};
|
|
}
|
|
}
|
|
}
|