Merge branch 'dev_add_auth_1029' into 'master'
Dev add auth 1029 See merge request ql/apismnagaer_backend!7
This commit is contained in:
commit
06a2a72485
@ -8,16 +8,14 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0" />
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0" />
|
||||||
|
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Tools\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
using Apimanager_backend.Services;
|
using Apimanager_backend.Services;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using StackExchange.Redis;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Apimanager_backend.Config
|
namespace Apimanager_backend.Config
|
||||||
{
|
{
|
||||||
@ -8,7 +13,35 @@ namespace Apimanager_backend.Config
|
|||||||
public static IServiceCollection AddAllService(this IServiceCollection services,IConfiguration configuration)
|
public static IServiceCollection AddAllService(this IServiceCollection services,IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
|
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
|
||||||
|
services.AddJWTService(configuration);
|
||||||
services.AddScoped<IUserService, UserService>();
|
services.AddScoped<IUserService, UserService>();
|
||||||
|
services.AddScoped<IAuthService, AuthService>();
|
||||||
|
services.AddSingleton<ITokenService, TokenService>();
|
||||||
|
services.AddSingleton<IRefreshTokenService, RefreshTokenService>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
public static IServiceCollection AddJWTService(this IServiceCollection services,IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var jwtSettings = configuration.GetSection("JwtSettings");
|
||||||
|
var key = Encoding.ASCII.GetBytes(jwtSettings["Secret"]);
|
||||||
|
// JWT配置
|
||||||
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
ValidIssuer = jwtSettings["Issuer"],
|
||||||
|
ValidAudience = jwtSettings["Audience"],
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(key)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//redis配置
|
||||||
|
services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect(configuration["Redis:ConnectionString"]));
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
112
Apimanager_backend/Controllers/AuthController.cs
Normal file
112
Apimanager_backend/Controllers/AuthController.cs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
using Apimanager_backend.Dtos;
|
||||||
|
using Apimanager_backend.Exceptions;
|
||||||
|
using Apimanager_backend.Models;
|
||||||
|
using Apimanager_backend.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]/[action]")]
|
||||||
|
[ApiController]
|
||||||
|
public class AuthController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IAuthService authService;
|
||||||
|
private readonly ITokenService tokenService;
|
||||||
|
private readonly IRefreshTokenService refreshTokenService;
|
||||||
|
private readonly IUserService userService;
|
||||||
|
public AuthController(IAuthService authService, ITokenService tokenService, IRefreshTokenService refreshTokenService,IUserService userService)
|
||||||
|
{
|
||||||
|
this.authService = authService;
|
||||||
|
this.tokenService = tokenService;
|
||||||
|
this.refreshTokenService = refreshTokenService;
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 用户登录控制器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto">登录信息</param>
|
||||||
|
/// <returns>通用返回信息格式</returns>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<ResponseBase<UserInfoDto>>> Login([FromBody] UserLoginDto dto)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UserInfoDto user = await authService.LoginAsync(dto.UserName, dto.Password);
|
||||||
|
//生成token
|
||||||
|
string token = tokenService.GenerateAccessToken(user.Id.ToString(),user.Roles);
|
||||||
|
//生成refreshtoken
|
||||||
|
string refreshToken = await refreshTokenService.CreateRefereshTokenAsync(user.Id.ToString());
|
||||||
|
var responseInfo = new ResponseBase<LoginResponseDto>(
|
||||||
|
code: 2000,
|
||||||
|
message: "Login successful",
|
||||||
|
data: new LoginResponseDto
|
||||||
|
{
|
||||||
|
UserInfo = user,
|
||||||
|
Token = token,
|
||||||
|
RefreshToken = refreshToken
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return Ok(responseInfo);
|
||||||
|
}
|
||||||
|
catch (BaseException e)
|
||||||
|
{
|
||||||
|
|
||||||
|
//错误时,构建错误信息对象
|
||||||
|
var responseInfo = new ResponseBase<object?>(
|
||||||
|
code: e.code,
|
||||||
|
message: e.message,
|
||||||
|
data: null
|
||||||
|
);
|
||||||
|
|
||||||
|
return e.code switch
|
||||||
|
{
|
||||||
|
2001 => Unauthorized(responseInfo),
|
||||||
|
2002 => Unauthorized(responseInfo),
|
||||||
|
_ => StatusCode(503)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<ResponseBase<RefreshResponseDto?>>> Refresh([FromBody]RefreshResponseDto dto)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userId = await refreshTokenService.ValidateRefreshTokenAsync(dto.RefreshToken);
|
||||||
|
//刷新令牌无效
|
||||||
|
if (userId == null)
|
||||||
|
{
|
||||||
|
var ret = new ResponseBase<RefreshResponseDto?>(
|
||||||
|
code: 2008,
|
||||||
|
message: "Refresh expires or is invalid",
|
||||||
|
data: null
|
||||||
|
);
|
||||||
|
return Unauthorized(ret);
|
||||||
|
}
|
||||||
|
//获取刷新令牌对应用户信息
|
||||||
|
var userInfo = await userService.GetUserAsync(int.Parse(userId));
|
||||||
|
//重新生成令牌
|
||||||
|
var token = tokenService.GenerateAccessToken(userInfo.Id.ToString(), userInfo.Roles);
|
||||||
|
//刷新刷新令牌有效期(小于三天才会刷新)
|
||||||
|
await refreshTokenService.UpdateRefreshTokenAsync(dto.RefreshToken);
|
||||||
|
var result = new ResponseBase<RefreshResponseDto?>(
|
||||||
|
code: 1000,
|
||||||
|
message: "Success",
|
||||||
|
data: new RefreshResponseDto
|
||||||
|
{
|
||||||
|
Token = token,
|
||||||
|
RefreshToken = dto.RefreshToken
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
return Ok(result);
|
||||||
|
}catch(BaseException e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,8 +7,7 @@ using Apimanager_backend.Filters;
|
|||||||
|
|
||||||
namespace Apimanager_backend.Controllers
|
namespace Apimanager_backend.Controllers
|
||||||
{
|
{
|
||||||
[ModelValidationFilter()]
|
[Route("api/[controller]/[action]")]
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class UserController : ControllerBase
|
public class UserController : ControllerBase
|
||||||
{
|
{
|
||||||
@ -17,37 +16,6 @@ namespace Apimanager_backend.Controllers
|
|||||||
{
|
{
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
/// <summary>
|
|
||||||
/// 用户登录控制器
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dto">登录信息</param>
|
|
||||||
/// <returns>通用返回信息格式</returns>
|
|
||||||
[HttpPost("Login")]
|
|
||||||
public async Task<ActionResult<ResponseBase<UserInfoDto>>> Login([FromBody]UserLoginDto dto)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
UserInfoDto user = await userService.LoginAsync(dto.UserName, dto.Password);
|
|
||||||
|
|
||||||
var responseInfo = new ResponseBase<UserInfoDto>(
|
|
||||||
code: 2000,
|
|
||||||
message: "Login successful",
|
|
||||||
data: user
|
|
||||||
);
|
|
||||||
return Ok(responseInfo);
|
|
||||||
}
|
|
||||||
catch (BaseException e)
|
|
||||||
{
|
|
||||||
|
|
||||||
//错误时,构建错误信息对象
|
|
||||||
var responseInfo = new ResponseBase<object?>(
|
|
||||||
code:e.code,
|
|
||||||
message: e.message,
|
|
||||||
data: null
|
|
||||||
);
|
|
||||||
|
|
||||||
return Unauthorized(responseInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,8 @@ namespace Apimanager_backend.Data
|
|||||||
public DbSet<Order> Orders { get; set; }
|
public DbSet<Order> Orders { get; set; }
|
||||||
//用户已订购套餐表
|
//用户已订购套餐表
|
||||||
public DbSet<UserPackage> UserPackages { get; set; }
|
public DbSet<UserPackage> UserPackages { get; set; }
|
||||||
|
//用户角色表
|
||||||
|
public DbSet<UserRole> UserRoles { get; set; }
|
||||||
public ApiContext(DbContextOptions<ApiContext> options) : base(options) { }
|
public ApiContext(DbContextOptions<ApiContext> options) : base(options) { }
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
|||||||
21
Apimanager_backend/Data/UserRoleConfig.cs
Normal file
21
Apimanager_backend/Data/UserRoleConfig.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Apimanager_backend.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Data
|
||||||
|
{
|
||||||
|
public class UserRoleConfig : IEntityTypeConfiguration<UserRole>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<UserRole> builder)
|
||||||
|
{
|
||||||
|
builder.HasKey(x => x.Id);
|
||||||
|
//自增
|
||||||
|
builder.Property(x => x.Id)
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
//外键
|
||||||
|
builder.HasOne(x => x.User)
|
||||||
|
.WithMany(u => u.Roles)
|
||||||
|
.HasForeignKey(x => x.UserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Apimanager_backend/Dtos/LoginResponseDto.cs
Normal file
11
Apimanager_backend/Dtos/LoginResponseDto.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Apimanager_backend.Models;
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Dtos
|
||||||
|
{
|
||||||
|
public class LoginResponseDto
|
||||||
|
{
|
||||||
|
public UserInfoDto UserInfo { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
|
public string RefreshToken { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Apimanager_backend/Dtos/RefreshResponseDto.cs
Normal file
11
Apimanager_backend/Dtos/RefreshResponseDto.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Dtos
|
||||||
|
{
|
||||||
|
public class RefreshResponseDto
|
||||||
|
{
|
||||||
|
public string? Token { get; set; }
|
||||||
|
[Required(ErrorMessage = "RefreshToken is required")]
|
||||||
|
public string RefreshToken { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Apimanager_backend/Dtos/UserInfoBaseDto.cs
Normal file
16
Apimanager_backend/Dtos/UserInfoBaseDto.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using Apimanager_backend.Models;
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Dtos
|
||||||
|
{
|
||||||
|
public class UserInfoBaseDto
|
||||||
|
{
|
||||||
|
public UserInfoDto UserInfo { get; set; }
|
||||||
|
public List<UserRole> Roles { get; set; }
|
||||||
|
public UserInfoBaseDto(UserInfoDto userInfo, List<UserRole> roles)
|
||||||
|
{
|
||||||
|
UserInfo = userInfo;
|
||||||
|
Roles = roles;
|
||||||
|
}
|
||||||
|
public UserInfoBaseDto() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,7 +7,7 @@ namespace Apimanager_backend.Dtos
|
|||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string UserName { get; set; }
|
public string UserName { get; set; }
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public UserRole Role { get; set; }
|
public List<UserRole> Roles { get; set; }
|
||||||
public bool IsBan { get; set; }
|
public bool IsBan { get; set; }
|
||||||
public decimal Balance { get; set; }
|
public decimal Balance { get; set; }
|
||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
using Apimanager_backend.Dtos;
|
||||||
|
using Apimanager_backend.Exceptions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Filters.ExceptionFilter
|
||||||
|
{
|
||||||
|
public class generalExceptionFilter : IExceptionFilter
|
||||||
|
{
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
if(context.Exception is BaseException)
|
||||||
|
{
|
||||||
|
//构造通用错误返回结果
|
||||||
|
BaseException exception = (BaseException)context.Exception;
|
||||||
|
var res = new ResponseBase<object?>(
|
||||||
|
code:exception.code,
|
||||||
|
message: exception.message,
|
||||||
|
data:null
|
||||||
|
);
|
||||||
|
//根据自定义错误码返回对应http状态码
|
||||||
|
int code = 0;
|
||||||
|
switch (exception.code)
|
||||||
|
{
|
||||||
|
case 1001:
|
||||||
|
case 1005:
|
||||||
|
case 2007:
|
||||||
|
case 4001:
|
||||||
|
code = 400;
|
||||||
|
break;
|
||||||
|
case 1002:
|
||||||
|
case 2001:
|
||||||
|
code = 401;
|
||||||
|
break;
|
||||||
|
case 1003:
|
||||||
|
case 2006:
|
||||||
|
case 3001:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc.Filters;
|
|||||||
|
|
||||||
namespace Apimanager_backend.Filters
|
namespace Apimanager_backend.Filters
|
||||||
{
|
{
|
||||||
public class ModelValidationFilter : Attribute,IActionFilter
|
public class ModelValidationFilter :IActionFilter
|
||||||
{
|
{
|
||||||
public void OnActionExecuted(ActionExecutedContext context) { }
|
public void OnActionExecuted(ActionExecutedContext context) { }
|
||||||
|
|
||||||
|
|||||||
408
Apimanager_backend/Migrations/20241030160723_add-userrole-table.Designer.cs
generated
Normal file
408
Apimanager_backend/Migrations/20241030160723_add-userrole-table.Designer.cs
generated
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Apimanager_backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApiContext))]
|
||||||
|
[Migration("20241030160723_add-userrole-table")]
|
||||||
|
partial class adduserroletable
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.0")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.Api", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Endpoint")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDelete")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsThirdParty")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<int>("Method")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)");
|
||||||
|
|
||||||
|
b.Property<int?>("PackageId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PackageId");
|
||||||
|
|
||||||
|
b.ToTable("Apis");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("ApiId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CallResult")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CallTime")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("CallLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CallLimit")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiryDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("varchar(20)");
|
||||||
|
|
||||||
|
b.Property<decimal>("Price")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Apipackages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(45)
|
||||||
|
.HasColumnType("varchar(45)");
|
||||||
|
|
||||||
|
b.Property<string>("Operation")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("varchar(20)");
|
||||||
|
|
||||||
|
b.Property<int>("TargetId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("TargetType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("varchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("OperationLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.Order", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("OrderNumber")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<int>("OrderType")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ThirdPartyOrderId")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("OrderNumber")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("ThirdPartyOrderId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Orders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("Balance")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsBan")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDelete")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("PassHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Email")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("Username")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.UserPackage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("PackageId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("PurchasedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("RemainingCalls")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PackageId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserPackages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Role")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.Api", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Apimanager_backend.Models.Apipackage", "Package")
|
||||||
|
.WithMany("Apis")
|
||||||
|
.HasForeignKey("PackageId");
|
||||||
|
|
||||||
|
b.Navigation("Package");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Apimanager_backend.Models.Api", "Api")
|
||||||
|
.WithMany("ApiCalls")
|
||||||
|
.HasForeignKey("ApiId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Apimanager_backend.Models.User", "User")
|
||||||
|
.WithMany("CallLogs")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Api");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Apimanager_backend.Models.User", "User")
|
||||||
|
.WithMany("operationLogs")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.Order", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Apimanager_backend.Models.User", "User")
|
||||||
|
.WithMany("Orders")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.UserPackage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Apimanager_backend.Models.Apipackage", "Package")
|
||||||
|
.WithMany("Packages")
|
||||||
|
.HasForeignKey("PackageId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Apimanager_backend.Models.User", "User")
|
||||||
|
.WithMany("Packages")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Package");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Apimanager_backend.Models.User", "User")
|
||||||
|
.WithMany("Roles")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.Api", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("ApiCalls");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Apis");
|
||||||
|
|
||||||
|
b.Navigation("Packages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("CallLogs");
|
||||||
|
|
||||||
|
b.Navigation("Orders");
|
||||||
|
|
||||||
|
b.Navigation("Packages");
|
||||||
|
|
||||||
|
b.Navigation("Roles");
|
||||||
|
|
||||||
|
b.Navigation("operationLogs");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class adduserroletable : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Role",
|
||||||
|
table: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
UserId = table.Column<int>(type: "int", nullable: false),
|
||||||
|
Role = table.Column<string>(type: "longtext", nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserRoles", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserRoles_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserRoles_UserId",
|
||||||
|
table: "UserRoles",
|
||||||
|
column: "UserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserRoles");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "Role",
|
||||||
|
table: "Users",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -233,9 +233,6 @@ namespace Apimanager_backend.Migrations
|
|||||||
.HasMaxLength(255)
|
.HasMaxLength(255)
|
||||||
.HasColumnType("varchar(255)");
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
b.Property<int>("Role")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
b.Property<string>("Username")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("varchar(255)");
|
.HasColumnType("varchar(255)");
|
||||||
@ -278,6 +275,26 @@ namespace Apimanager_backend.Migrations
|
|||||||
b.ToTable("UserPackages");
|
b.ToTable("UserPackages");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Role")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Apimanager_backend.Models.Api", b =>
|
modelBuilder.Entity("Apimanager_backend.Models.Api", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Apimanager_backend.Models.Apipackage", "Package")
|
b.HasOne("Apimanager_backend.Models.Apipackage", "Package")
|
||||||
@ -347,6 +364,17 @@ namespace Apimanager_backend.Migrations
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Apimanager_backend.Models.UserRole", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Apimanager_backend.Models.User", "User")
|
||||||
|
.WithMany("Roles")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Apimanager_backend.Models.Api", b =>
|
modelBuilder.Entity("Apimanager_backend.Models.Api", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("ApiCalls");
|
b.Navigation("ApiCalls");
|
||||||
@ -367,6 +395,8 @@ namespace Apimanager_backend.Migrations
|
|||||||
|
|
||||||
b.Navigation("Packages");
|
b.Navigation("Packages");
|
||||||
|
|
||||||
|
b.Navigation("Roles");
|
||||||
|
|
||||||
b.Navigation("operationLogs");
|
b.Navigation("operationLogs");
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
|
|||||||
@ -30,7 +30,7 @@ namespace Apimanager_backend.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户角色
|
/// 用户角色
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UserRole Role { get; set; } // Enum('Admin','User')
|
//public UserRole Role { get; set; } // Enum('Admin','User')
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否禁用
|
/// 是否禁用
|
||||||
@ -56,5 +56,6 @@ namespace Apimanager_backend.Models
|
|||||||
public ICollection<OperationLog> operationLogs { get; set; }
|
public ICollection<OperationLog> operationLogs { get; set; }
|
||||||
public ICollection<ApiCallLog> CallLogs { get; set; }
|
public ICollection<ApiCallLog> CallLogs { get; set; }
|
||||||
public ICollection<Order> Orders { get; set; }
|
public ICollection<Order> Orders { get; set; }
|
||||||
|
public ICollection<UserRole> Roles { get; set; } = new List<UserRole>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,15 @@
|
|||||||
namespace Apimanager_backend.Models
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Apimanager_backend.Models
|
||||||
{
|
{
|
||||||
public enum UserRole
|
public class UserRole
|
||||||
{
|
{
|
||||||
Admin = 0,
|
public int Id { get; set; }
|
||||||
User = 1
|
public int UserId { get; set; }
|
||||||
|
public string Role { get; set; }
|
||||||
|
|
||||||
|
//导航属性
|
||||||
|
[JsonIgnore]
|
||||||
|
public User User { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,15 @@ builder.Services.AddAllService(configuration);
|
|||||||
builder.Services.AddControllers(options =>
|
builder.Services.AddControllers(options =>
|
||||||
{
|
{
|
||||||
options.Filters.Add<ModelValidationFilter>();
|
options.Filters.Add<ModelValidationFilter>();
|
||||||
|
}).ConfigureApiBehaviorOptions(option =>
|
||||||
|
{
|
||||||
|
option.SuppressModelStateInvalidFilter = true;
|
||||||
|
})
|
||||||
|
.AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
options.JsonSerializerOptions.MaxDepth = 64;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
@ -37,6 +45,7 @@ if (app.Environment.IsDevelopment())
|
|||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|||||||
41
Apimanager_backend/Services/AuthService.cs
Normal file
41
Apimanager_backend/Services/AuthService.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Apimanager_backend/Services/IAuthService.cs
Normal file
15
Apimanager_backend/Services/IAuthService.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Apimanager_backend/Services/IRefreshTokenService.cs
Normal file
30
Apimanager_backend/Services/IRefreshTokenService.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Apimanager_backend/Services/ITokenService.cs
Normal file
16
Apimanager_backend/Services/ITokenService.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,13 +6,7 @@ namespace Apimanager_backend.Services
|
|||||||
{
|
{
|
||||||
public interface IUserService
|
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>
|
/// <summary>
|
||||||
/// 发送密码重置邮件到指定邮箱。
|
/// 发送密码重置邮件到指定邮箱。
|
||||||
@ -33,9 +27,9 @@ namespace Apimanager_backend.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取用户信息。
|
/// 获取用户信息。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="username">用户名</param>
|
/// <param name="userId">用户ID</param>
|
||||||
/// <returns>包含用户信息的 <see cref="UserInfoDto"/></returns>
|
/// <returns>包含用户信息的 <see cref="UserInfoDto"/></returns>
|
||||||
Task<UserInfoDto> GetUserAsync(string username);
|
Task<UserInfoDto> GetUserAsync(int userId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新用户信息。
|
/// 更新用户信息。
|
||||||
|
|||||||
68
Apimanager_backend/Services/RefreshTokenService.cs
Normal file
68
Apimanager_backend/Services/RefreshTokenService.cs
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
Apimanager_backend/Services/TokenService.cs
Normal file
50
Apimanager_backend/Services/TokenService.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -34,9 +34,15 @@ namespace Apimanager_backend.Services
|
|||||||
throw new NotImplementedException();
|
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)
|
public Task<List<UserInfoDto>> GetUsersAsync(int page, int pageSize, bool desc)
|
||||||
@ -44,27 +50,6 @@ namespace Apimanager_backend.Services
|
|||||||
throw new NotImplementedException();
|
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)
|
public Task ResetPasswordAsync(string email, string token, string newPassword)
|
||||||
{
|
{
|
||||||
|
|||||||
6
Apimanager_backend/Tools/HashHelper.cs
Normal file
6
Apimanager_backend/Tools/HashHelper.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Apimanager_backend.Tools
|
||||||
|
{
|
||||||
|
public class HashHelper
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,5 +8,15 @@
|
|||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "server=192.168.5.200;username=root;password=768788Dyw@;port=3306;database=api_billing_system;SslMode=Preferred;"
|
"DefaultConnection": "server=192.168.5.200;username=root;password=768788Dyw@;port=3306;database=api_billing_system;SslMode=Preferred;"
|
||||||
|
},
|
||||||
|
"JwtSettings": {
|
||||||
|
"Secret": "deXtdXode6hv0SI1o6xRw1ALkn0vYsWn",
|
||||||
|
"Issuer": "qinglan",
|
||||||
|
"Audience": "qinglan",
|
||||||
|
"AccessTokenExpiryMinutes": 60,
|
||||||
|
"RefreshTokenExpiryDays": 7
|
||||||
|
},
|
||||||
|
"redis": {
|
||||||
|
"ConnectionString": "192.168.5.200:6379"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
ErrorCode.md
22
ErrorCode.md
@ -14,16 +14,18 @@
|
|||||||
|
|
||||||
#### 用户模块错误码(2xxx)
|
#### 用户模块错误码(2xxx)
|
||||||
|
|
||||||
| 错误码 | HTTP状态码 | 描述 | Message |
|
| 错误码 | HTTP状态码 | 描述 | Message |
|
||||||
| ------ | ---------- | -------------------- | ---------------------------- |
|
| ------ | ---------- | -------------------- | ----------------------------- |
|
||||||
| 2000 | 200 | 登录成功 | Login successful |
|
| 2000 | 200 | 登录成功 | Login successful |
|
||||||
| 2001 | 401 | 用户名或密码错误 | Invalid username or password |
|
| 2001 | 401 | 用户名或密码错误 | Invalid username or password |
|
||||||
| 2002 | 401 | 用户账户被禁用 | User account is disabled |
|
| 2002 | 401 | 用户账户被禁用 | User account is disabled |
|
||||||
| 2003 | 409 | 用户名已存在 | Username already exists |
|
| 2003 | 409 | 用户名已存在 | Username already exists |
|
||||||
| 2004 | 404 | 用户不存在 | User not found |
|
| 2004 | 404 | 用户不存在 | User not found |
|
||||||
| 2005 | 409 | 邮箱已存在 | Email already exists |
|
| 2005 | 409 | 邮箱已存在 | Email already exists |
|
||||||
| 2006 | 403 | 用户无权限进行该操作 | Permission denied |
|
| 2006 | 403 | 用户无权限进行该操作 | Permission denied |
|
||||||
| 2007 | 400 | 密码重置失败 | Password reset failed |
|
| 2007 | 400 | 密码重置失败 | Password reset failed |
|
||||||
|
| 2008 | 403 | 凭证到期或无效 | Token expires or is invalid |
|
||||||
|
| 2009 | 403 | 刷新令牌到期或无效 | Refresh expires or is invalid |
|
||||||
|
|
||||||
#### API模块错误码(3xxx)
|
#### API模块错误码(3xxx)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user