添加jwt
This commit is contained in:
parent
977972b029
commit
b58cc5f229
|
|
@ -7,15 +7,28 @@
|
||||||
<UserSecretsId>3936c093-f18d-46a7-91d6-96cc43ffe3b8</UserSecretsId>
|
<UserSecretsId>3936c093-f18d-46a7-91d6-96cc43ffe3b8</UserSecretsId>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
<DockerfileContext>..\..</DockerfileContext>
|
<DockerfileContext>..\..</DockerfileContext>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<DebugType>embedded</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<DebugType>embedded</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Code\CloudGaming.Code\CloudGaming.Code.csproj" />
|
<ProjectReference Include="..\..\Code\CloudGaming.Code\CloudGaming.Code.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Model\CloudGaming.DtoModel\CloudGaming.DtoModel.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Model\CloudGaming.GameModel\CloudGaming.GameModel.csproj" />
|
||||||
<ProjectReference Include="..\..\Model\CloudGaming.Model\CloudGaming.Model.csproj" />
|
<ProjectReference Include="..\..\Model\CloudGaming.Model\CloudGaming.Model.csproj" />
|
||||||
<ProjectReference Include="..\..\Utile\HuanMeng.DotNetCore\HuanMeng.DotNetCore.csproj" />
|
<ProjectReference Include="..\..\Utile\HuanMeng.DotNetCore\HuanMeng.DotNetCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,136 @@
|
||||||
|
using CloudGaming.Code.AppExtend;
|
||||||
|
using CloudGaming.Code.DataAccess.MultiTenantUtil;
|
||||||
|
using HuanMeng.DotNetCore.MiddlewareExtend;
|
||||||
|
using HuanMeng.DotNetCore.CustomExtension;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
using HuanMeng.DotNetCore.Utility.AssemblyHelper;
|
||||||
|
using HuanMeng.DotNetCore.SwaggerUtile;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers()
|
||||||
|
.AddNewtonsoftJson(options =>
|
||||||
|
{
|
||||||
|
// 配置 Newtonsoft.Json 选项
|
||||||
|
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; // 忽略循环引用
|
||||||
|
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();// 首字母小写(驼峰样式)
|
||||||
|
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";// 时间格式化
|
||||||
|
#if !DEBUG
|
||||||
|
options.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.None;
|
||||||
|
#endif
|
||||||
|
//options.SerializerSettings.Converters.Add()
|
||||||
|
// 其他配置...
|
||||||
|
});
|
||||||
// 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(c =>
|
||||||
|
{
|
||||||
|
|
||||||
|
var securityScheme = new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Name = "JWT 身份验证(Authentication)",
|
||||||
|
Description = "请输入登录后获取JWT的**token**",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Type = SecuritySchemeType.Http,
|
||||||
|
Scheme = "bearer", //必须小写
|
||||||
|
BearerFormat = "JWT",
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Id = JwtBearerDefaults.AuthenticationScheme,
|
||||||
|
Type = ReferenceType.SecurityScheme
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
|
||||||
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{securityScheme, Array.Empty<string>()}
|
||||||
|
});
|
||||||
|
|
||||||
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "蒸汽云游戏", Version = "1.0.0", Description = "" });
|
||||||
|
foreach (var assemblies in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
// 添加 XML 注释文件路径
|
||||||
|
var xmlFile = $"{assemblies.GetName().Name}.xml";
|
||||||
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
||||||
|
if (File.Exists(xmlPath))
|
||||||
|
{
|
||||||
|
c.IncludeXmlComments(xmlPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.ParameterFilter<LowercaseParameterFilter>();
|
||||||
|
c.RequestBodyFilter<LowercaseRequestFilter>();
|
||||||
|
});
|
||||||
|
builder.AddAppConfigClient();
|
||||||
|
//添加jwt验证
|
||||||
|
builder.AddJwtConfig();
|
||||||
|
#region 添加跨域
|
||||||
|
var _myAllowSpecificOrigins = "_myAllowSpecificOrigins";
|
||||||
|
builder.Services.AddCustomCors(_myAllowSpecificOrigins);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
//添加jwt验证
|
||||||
|
//builder.AddJwtConfig();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (app.Environment.IsDevelopment())
|
//if (app.Environment.IsDevelopment())
|
||||||
|
//{
|
||||||
|
// app.UseSwagger();
|
||||||
|
// app.UseSwaggerUI();
|
||||||
|
//}
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI(c =>
|
||||||
{
|
{
|
||||||
app.UseSwagger();
|
|
||||||
app.UseSwaggerUI();
|
c.EnableDeepLinking();
|
||||||
}
|
c.DefaultModelsExpandDepth(3);
|
||||||
|
c.DefaultModelExpandDepth(3);
|
||||||
|
c.EnableFilter("true");
|
||||||
|
//c.RoutePrefix = string.Empty;
|
||||||
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "蒸汽云游戏 API V1");
|
||||||
|
});
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
//数据库中间件
|
||||||
|
app.UseMultiTenant();
|
||||||
|
//使用跨域
|
||||||
|
app.UseCors(_myAllowSpecificOrigins);
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
app.UseStaticFiles();//静态文件访问配置
|
||||||
|
//执行扩展中间件
|
||||||
|
app.UseMiddlewareAll();
|
||||||
|
#region 默认请求
|
||||||
|
app.MapGet("/", () => "请求成功").WithName("默认请求");
|
||||||
|
|
||||||
|
var startDateTime = DateTime.Now;
|
||||||
|
var InformationalVersion = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
||||||
|
Console.WriteLine($"version:{InformationalVersion}");
|
||||||
|
app.MapGet("/system", () =>
|
||||||
|
{
|
||||||
|
|
||||||
|
using Process currentProcess = Process.GetCurrentProcess();
|
||||||
|
// CPU使用率 (一般是一个0-100之间的值,但实际是时间占比,需要转换)
|
||||||
|
double cpuUsage = currentProcess.TotalProcessorTime.TotalMilliseconds / Environment.TickCount * 100;
|
||||||
|
// 已用内存 (字节)
|
||||||
|
long memoryUsage = currentProcess.WorkingSet64;
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
msg = $"系统信息:启动时间:{startDateTime.ToString("yyyy-MM-dd HH:mm:ss")},已安全运行时间:{DateTime.Now.Subtract(startDateTime).TotalMinutes.ToString("0.##")}分钟",
|
||||||
|
|
||||||
|
startDateTime,
|
||||||
|
MemoryUsage = $"{memoryUsage / (1024.0 * 1024.0):F2}MB",
|
||||||
|
CPUUsage = $"{cpuUsage:F2}%"
|
||||||
|
|
||||||
|
};
|
||||||
|
}).WithName("获取系统数据");
|
||||||
|
#endregion
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.CreateDataBase"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.GameModel", "Model\CloudGaming.GameModel\CloudGaming.GameModel.csproj", "{1120C146-6B83-4E4E-8A39-BD09466C7E1B}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.GameModel", "Model\CloudGaming.GameModel\CloudGaming.GameModel.csproj", "{1120C146-6B83-4E4E-8A39-BD09466C7E1B}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.DtoModel", "Model\CloudGaming.DtoModel\CloudGaming.DtoModel.csproj", "{96CD0865-0AD5-41B3-89A2-374FF17CDD16}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -62,6 +64,10 @@ Global
|
||||||
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{96CD0865-0AD5-41B3-89A2-374FF17CDD16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{96CD0865-0AD5-41B3-89A2-374FF17CDD16}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{96CD0865-0AD5-41B3-89A2-374FF17CDD16}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{96CD0865-0AD5-41B3-89A2-374FF17CDD16}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -73,6 +79,7 @@ Global
|
||||||
{5F851D79-E435-4D16-974A-6D5E3A3269A7} = {FCA3CA4B-1993-429A-B2E9-2B05DB44F10E}
|
{5F851D79-E435-4D16-974A-6D5E3A3269A7} = {FCA3CA4B-1993-429A-B2E9-2B05DB44F10E}
|
||||||
{393ED915-3F88-4F84-AE2A-5C95F8867F16} = {9F7EF36C-17BB-4F93-927E-F462FE3C9337}
|
{393ED915-3F88-4F84-AE2A-5C95F8867F16} = {9F7EF36C-17BB-4F93-927E-F462FE3C9337}
|
||||||
{1120C146-6B83-4E4E-8A39-BD09466C7E1B} = {A3F00FB0-49D6-48B1-99D9-4619634DF8D9}
|
{1120C146-6B83-4E4E-8A39-BD09466C7E1B} = {A3F00FB0-49D6-48B1-99D9-4619634DF8D9}
|
||||||
|
{96CD0865-0AD5-41B3-89A2-374FF17CDD16} = {A3F00FB0-49D6-48B1-99D9-4619634DF8D9}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {1D299D92-FA27-47A0-8D78-43D1FAFE7628}
|
SolutionGuid = {1D299D92-FA27-47A0-8D78-43D1FAFE7628}
|
||||||
|
|
|
||||||
109
src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
Normal file
109
src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.AppExtend
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 项目配置
|
||||||
|
/// </summary>
|
||||||
|
public class AppConfig
|
||||||
|
{
|
||||||
|
public AppConfig() { }
|
||||||
|
/// <summary>
|
||||||
|
/// 用户数据库连接字符串
|
||||||
|
/// </summary>
|
||||||
|
public string UserConnectionString { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏
|
||||||
|
/// </summary>
|
||||||
|
public string GameConnectionString { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 扩展
|
||||||
|
/// </summary>
|
||||||
|
public string ExtConnectionString { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 手机app配置
|
||||||
|
/// </summary>
|
||||||
|
public string PhoneConnectionString { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// redis连接字符串
|
||||||
|
/// </summary>
|
||||||
|
public string RedisConnectionString { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 域名
|
||||||
|
/// </summary>
|
||||||
|
public string DomainName { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 标识
|
||||||
|
/// </summary>
|
||||||
|
public string Identifier { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 租户
|
||||||
|
/// </summary>
|
||||||
|
public Guid TenantId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 项目支付数据
|
||||||
|
/// </summary>
|
||||||
|
//public PaymentModel? Payment { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取数据库连接字符串
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">user,game,ext,phone</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public string GetConnectionString(AppDataBaseType appDataBaseType)
|
||||||
|
{
|
||||||
|
switch (appDataBaseType)
|
||||||
|
{
|
||||||
|
case AppDataBaseType.User:
|
||||||
|
return UserConnectionString;
|
||||||
|
case AppDataBaseType.Game:
|
||||||
|
return GameConnectionString;
|
||||||
|
case AppDataBaseType.Ext:
|
||||||
|
return ExtConnectionString;
|
||||||
|
case AppDataBaseType.App:
|
||||||
|
return PhoneConnectionString;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException("数据库连接字符串不存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据库选项
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
public enum AppDataBaseType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户数据库
|
||||||
|
/// </summary>
|
||||||
|
User,
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏数据库
|
||||||
|
/// </summary>
|
||||||
|
Game,
|
||||||
|
/// <summary>
|
||||||
|
/// 扩展数据库
|
||||||
|
/// </summary>
|
||||||
|
Ext,
|
||||||
|
/// <summary>
|
||||||
|
/// app数据库
|
||||||
|
/// </summary>
|
||||||
|
App
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
using CloudGaming.Code.DataAccess.MultiTenantUtil;
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Frozen;
|
||||||
|
|
||||||
|
using XLib.DotNetCore.CacheHelper;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.AppExtend
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// app配置项扩展
|
||||||
|
/// </summary>
|
||||||
|
public static class AppConfigurationExtend
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置数据
|
||||||
|
/// </summary>
|
||||||
|
//public static ConfigClient AppConfigClient { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static IConfigurationManager ConfigurationManager { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static ConcurrentDictionary<string, AppConfig> AppConfigs { get; set; } = new ConcurrentDictionary<string, AppConfig>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取配置项
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="domainName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static AppConfig GetAppConfig(string domainName)
|
||||||
|
{
|
||||||
|
if (AppConfigs.TryGetValue(domainName, out var appConfig))
|
||||||
|
{
|
||||||
|
|
||||||
|
return appConfig;
|
||||||
|
}
|
||||||
|
if (AppConfigs.TryGetValue("default", out appConfig))
|
||||||
|
{
|
||||||
|
return appConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppConfigs.FirstOrDefault().Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化app
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IHostApplicationBuilder AddAppConfigClient(this IHostApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
|
||||||
|
//hostBuilder.
|
||||||
|
//builder..UseAgileConfig(configClient, ConfigClient_ConfigChanged);
|
||||||
|
//AppConfigClient = configClient;
|
||||||
|
ConfigurationManager = builder.Configuration;
|
||||||
|
AppConfigInit(builder.Configuration);
|
||||||
|
builder.Services.AddScoped<AppConfig>();
|
||||||
|
builder.AddDataBase();
|
||||||
|
return builder;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取配置项
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identifier"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static AppConfig? GetAppConfigIdentifier(string identifier)
|
||||||
|
{
|
||||||
|
var app = AppConfigs.Where(it => it.Value.Identifier == identifier).Select(it => it.Value).FirstOrDefault();
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置版本号
|
||||||
|
/// </summary>
|
||||||
|
public static string AppVersion
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region 租户
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appConfig"></param>
|
||||||
|
/// <param name="tenantInfo"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ITenantInfo ToITenantInfo(this AppConfig appConfig, ITenantInfo? tenantInfo = null)
|
||||||
|
{
|
||||||
|
if (tenantInfo == null)
|
||||||
|
{
|
||||||
|
tenantInfo = new TenantInfo();
|
||||||
|
}
|
||||||
|
tenantInfo.TenantId = appConfig.TenantId;
|
||||||
|
tenantInfo.Identifier = appConfig.Identifier;
|
||||||
|
tenantInfo.Name = appConfig.Name;
|
||||||
|
return tenantInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appConfig"></param>
|
||||||
|
/// <param name="newAppConfig"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static AppConfig ToAppConfig(this AppConfig appConfig, AppConfig? newAppConfig = null)
|
||||||
|
{
|
||||||
|
if (newAppConfig == null)
|
||||||
|
{
|
||||||
|
newAppConfig = new AppConfig();
|
||||||
|
}
|
||||||
|
if (appConfig == null)
|
||||||
|
{
|
||||||
|
appConfig = new AppConfig();
|
||||||
|
}
|
||||||
|
newAppConfig.TenantId = appConfig.TenantId;
|
||||||
|
newAppConfig.Identifier = appConfig.Identifier;
|
||||||
|
newAppConfig.Name = appConfig.Name;
|
||||||
|
newAppConfig.DomainName = appConfig.DomainName;
|
||||||
|
newAppConfig.RedisConnectionString = appConfig.RedisConnectionString;
|
||||||
|
newAppConfig.UserConnectionString = appConfig.UserConnectionString;
|
||||||
|
newAppConfig.GameConnectionString = appConfig.GameConnectionString;
|
||||||
|
newAppConfig.ExtConnectionString = appConfig.ExtConnectionString;
|
||||||
|
newAppConfig.PhoneConnectionString = appConfig.PhoneConnectionString;
|
||||||
|
return newAppConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化租户数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configurationManager"></param>
|
||||||
|
private static void AppConfigInit(IConfigurationManager configurationManager)
|
||||||
|
{
|
||||||
|
var tenants = configurationManager.GetSection("Tenant").Get<List<AppConfig>>();
|
||||||
|
if (tenants != null)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<string, AppConfig> _AppConfigs = new ConcurrentDictionary<string, AppConfig>();
|
||||||
|
if (tenants.Count > 0)
|
||||||
|
{
|
||||||
|
tenants?.ForEach(t =>
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!_AppConfigs.TryAdd(t.DomainName, t))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{t.DomainName}配置加载失败");
|
||||||
|
}
|
||||||
|
if (t.Name == "default")
|
||||||
|
{
|
||||||
|
_AppConfigs.TryAdd("default", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!_AppConfigs.TryGetValue("default", out var x))
|
||||||
|
{
|
||||||
|
_AppConfigs.TryAdd("default", tenants[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_AppConfigs.TryAdd("default", new AppConfig());
|
||||||
|
}
|
||||||
|
AppConfigs = _AppConfigs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
using HuanMeng.DotNetCore.JwtInfrastructure.Interface;
|
||||||
|
using HuanMeng.DotNetCore.JwtInfrastructure;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.AppExtend
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class JwtTokenManageExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加jwt安全验证,配置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="server"></param>
|
||||||
|
/// <param name="configuration"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static void AddJwtConfig(this IHostApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
var jwtTokenConfig = builder.Configuration.GetSection("JwtTokenConfig").Get<JwtTokenConfig>()!;
|
||||||
|
if (jwtTokenConfig == null)
|
||||||
|
{
|
||||||
|
jwtTokenConfig = new JwtTokenConfig()
|
||||||
|
{
|
||||||
|
Secret = Guid.NewGuid().ToString(),
|
||||||
|
AccessTokenExpiration = 60 * 60
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//注册一个jwtTokenConfig的单例服务
|
||||||
|
builder.Services.AddSingleton(jwtTokenConfig);
|
||||||
|
//
|
||||||
|
builder.Services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
}).AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.RequireHttpsMetadata = true;
|
||||||
|
options.SaveToken = true;
|
||||||
|
//调试使用
|
||||||
|
//options.Events = new JwtDebugBearerEvents().GetJwtBearerEvents();
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
//是否验证颁发者
|
||||||
|
ValidateIssuer = true,
|
||||||
|
//是否验证受众
|
||||||
|
ValidateAudience = true,
|
||||||
|
//指定是否验证令牌的生存期。设置为 true 表示要进行验证。
|
||||||
|
ValidateLifetime = true,
|
||||||
|
//指定是否验证颁发者签名密钥。设置为 true 表示要进行验证。
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
//颁发者
|
||||||
|
ValidIssuer = jwtTokenConfig.Issuer,
|
||||||
|
//受众
|
||||||
|
ValidAudience = jwtTokenConfig.Audience,
|
||||||
|
//指定用于验证颁发者签名的密钥
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenConfig.Secret)),
|
||||||
|
//指定允许令牌的时钟偏移。允许令牌的过期时间与实际时间之间存在的时间差。在这里设置为 5 分钟,表示允许令牌的时钟偏移为 5 分钟。
|
||||||
|
ClockSkew = TimeSpan.FromMinutes(5)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
//注册一个JwtAuthManager的单例服务
|
||||||
|
builder.Services.AddSingleton<IJwtAuthManager, JwtManager>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,9 +4,17 @@
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Model\CloudGaming.DtoModel\CloudGaming.DtoModel.csproj" />
|
||||||
<ProjectReference Include="..\..\Model\CloudGaming.GameModel\CloudGaming.GameModel.csproj" />
|
<ProjectReference Include="..\..\Model\CloudGaming.GameModel\CloudGaming.GameModel.csproj" />
|
||||||
<ProjectReference Include="..\..\Model\CloudGaming.Model\CloudGaming.Model.csproj" />
|
<ProjectReference Include="..\..\Model\CloudGaming.Model\CloudGaming.Model.csproj" />
|
||||||
<ProjectReference Include="..\..\Utile\HuanMeng.DotNetCore\HuanMeng.DotNetCore.csproj" />
|
<ProjectReference Include="..\..\Utile\HuanMeng.DotNetCore\HuanMeng.DotNetCore.csproj" />
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,4 @@
|
||||||
using HuanMeng.DotNetCore.Base;
|
|
||||||
using HuanMeng.DotNetCore.MultiTenant.Contract;
|
|
||||||
using HuanMeng.DotNetCore.MultiTenant;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CloudGaming.GameModel.Db.Db_Ext;
|
|
||||||
using CloudGaming.GameModel.Db.Db_Game;
|
|
||||||
using CloudGaming.Model.DbSqlServer.Db_Phone;
|
|
||||||
using CloudGaming.Model.DbSqlServer.Db_User;
|
|
||||||
|
|
||||||
namespace CloudGaming.Code.DataAccess
|
namespace CloudGaming.Code.DataAccess
|
||||||
{
|
{
|
||||||
|
|
@ -20,8 +7,7 @@ namespace CloudGaming.Code.DataAccess
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DAO : DaoBase
|
public class DAO : DaoBase
|
||||||
{
|
{
|
||||||
//private IMultiTenantProvider _multiTenantProvider;
|
private ITenantInfo? _tenantInfo;
|
||||||
private ITenantInfo _tenantInfo;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
|
|
@ -32,7 +18,8 @@ namespace CloudGaming.Code.DataAccess
|
||||||
{
|
{
|
||||||
if (_tenantInfo == null)
|
if (_tenantInfo == null)
|
||||||
{
|
{
|
||||||
this._tenantInfo = _serviceProvider.GetRequiredService<TenantInfo>();
|
var app = _serviceProvider.GetRequiredService<AppConfig>();
|
||||||
|
this._tenantInfo = app.ToITenantInfo();
|
||||||
}
|
}
|
||||||
return this._tenantInfo;
|
return this._tenantInfo;
|
||||||
}
|
}
|
||||||
|
|
@ -43,16 +30,16 @@ namespace CloudGaming.Code.DataAccess
|
||||||
/// <param name="serviceProvider"></param>
|
/// <param name="serviceProvider"></param>
|
||||||
public DAO(IServiceProvider serviceProvider) : base(serviceProvider)
|
public DAO(IServiceProvider serviceProvider) : base(serviceProvider)
|
||||||
{
|
{
|
||||||
this._tenantInfo = serviceProvider.GetRequiredService<ITenantInfo>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serviceProvider"></param>
|
/// <param name="serviceProvider"></param>
|
||||||
public DAO(IServiceProvider serviceProvider, ITenantInfo tenantInfo) : base(serviceProvider)
|
public DAO(IServiceProvider serviceProvider, AppConfig appConfig) : base(serviceProvider)
|
||||||
{
|
{
|
||||||
this._tenantInfo = tenantInfo;
|
this._tenantInfo = appConfig.ToITenantInfo();
|
||||||
|
|
||||||
}
|
}
|
||||||
#region 扩展
|
#region 扩展
|
||||||
|
|
@ -61,7 +48,7 @@ namespace CloudGaming.Code.DataAccess
|
||||||
/// 数据库[CloudGamingCBT],
|
/// 数据库[CloudGamingCBT],
|
||||||
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
|
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EfCoreDaoBase<CloudGamingCBTContext> daoExt
|
public EfCoreDaoBase<CloudGamingCBTContext> DaoExt
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
@ -69,7 +56,7 @@ namespace CloudGaming.Code.DataAccess
|
||||||
{
|
{
|
||||||
var dbContext = _serviceProvider.GetRequiredService<CloudGamingCBTContext>();
|
var dbContext = _serviceProvider.GetRequiredService<CloudGamingCBTContext>();
|
||||||
_daoExt = new EfCoreDaoBase<CloudGamingCBTContext>(dbContext);
|
_daoExt = new EfCoreDaoBase<CloudGamingCBTContext>(dbContext);
|
||||||
_daoExt.context.TenantInfo = TenantInfo;
|
_daoExt.Context.TenantInfo = TenantInfo;
|
||||||
}
|
}
|
||||||
return _daoExt;
|
return _daoExt;
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +68,7 @@ namespace CloudGaming.Code.DataAccess
|
||||||
/// 数据库[CloudGamingGameContext],
|
/// 数据库[CloudGamingGameContext],
|
||||||
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
|
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EfCoreDaoBase<CloudGamingGameContext> daoGame
|
public EfCoreDaoBase<CloudGamingGameContext> DaoGame
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
@ -89,7 +76,7 @@ namespace CloudGaming.Code.DataAccess
|
||||||
{
|
{
|
||||||
var dbContext = _serviceProvider.GetRequiredService<CloudGamingGameContext>();
|
var dbContext = _serviceProvider.GetRequiredService<CloudGamingGameContext>();
|
||||||
_daoGame = new EfCoreDaoBase<CloudGamingGameContext>(dbContext);
|
_daoGame = new EfCoreDaoBase<CloudGamingGameContext>(dbContext);
|
||||||
_daoGame.context.TenantInfo = TenantInfo;
|
_daoGame.Context.TenantInfo = TenantInfo;
|
||||||
}
|
}
|
||||||
return _daoGame;
|
return _daoGame;
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +89,7 @@ namespace CloudGaming.Code.DataAccess
|
||||||
/// 数据库[CloudGamingGameContext],
|
/// 数据库[CloudGamingGameContext],
|
||||||
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
|
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EfCoreDaoBase<CloudGamingPhoneContext> daoPhone
|
public EfCoreDaoBase<CloudGamingPhoneContext> DaoPhone
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
@ -110,20 +97,20 @@ namespace CloudGaming.Code.DataAccess
|
||||||
{
|
{
|
||||||
var dbContext = _serviceProvider.GetRequiredService<CloudGamingPhoneContext>();
|
var dbContext = _serviceProvider.GetRequiredService<CloudGamingPhoneContext>();
|
||||||
_daoPhone = new EfCoreDaoBase<CloudGamingPhoneContext>(dbContext);
|
_daoPhone = new EfCoreDaoBase<CloudGamingPhoneContext>(dbContext);
|
||||||
_daoPhone.context.TenantInfo = TenantInfo;
|
_daoPhone.Context.TenantInfo = TenantInfo;
|
||||||
}
|
}
|
||||||
return _daoPhone;
|
return _daoPhone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 用户
|
#region 用户
|
||||||
private EfCoreDaoBase<CloudGamingUserContext>? _daoUser;
|
private EfCoreDaoBase<CloudGamingUserContext>? _daoUser;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据库[CloudGamingGameContext],
|
/// 数据库[CloudGamingGameContext],
|
||||||
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
|
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EfCoreDaoBase<CloudGamingUserContext> daoUser
|
public EfCoreDaoBase<CloudGamingUserContext> DaoUser
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
@ -131,7 +118,7 @@ namespace CloudGaming.Code.DataAccess
|
||||||
{
|
{
|
||||||
var dbContext = _serviceProvider.GetRequiredService<CloudGamingUserContext>();
|
var dbContext = _serviceProvider.GetRequiredService<CloudGamingUserContext>();
|
||||||
_daoUser = new EfCoreDaoBase<CloudGamingUserContext>(dbContext);
|
_daoUser = new EfCoreDaoBase<CloudGamingUserContext>(dbContext);
|
||||||
_daoUser.context.TenantInfo = TenantInfo;
|
_daoUser.Context.TenantInfo = TenantInfo;
|
||||||
}
|
}
|
||||||
return _daoUser;
|
return _daoUser;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
namespace CloudGaming.Code.DataAccess.MultiTenantUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 多租户扩展
|
||||||
|
/// </summary>
|
||||||
|
public static class MultiTenantExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 多租户 IServiceCollection 扩展
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
public static void AddDataBase(this IHostApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
// 获取初始连接字符串
|
||||||
|
string userConnectionString = builder.Configuration.GetConnectionString("UserConnectionString") ?? "";
|
||||||
|
string gameConnectionString = builder.Configuration.GetConnectionString("GameConnectionString") ?? "";
|
||||||
|
string phoneConnectionString = builder.Configuration.GetConnectionString("PhoneConnectionString") ?? "";
|
||||||
|
string extConnectionString = builder.Configuration.GetConnectionString("ExtConnectionString") ?? "";
|
||||||
|
|
||||||
|
// 注册 用户DbContext
|
||||||
|
RegisterDbContext<CloudGamingUserContext>(builder, userConnectionString, AppDataBaseType.User);
|
||||||
|
//注册 游戏Db
|
||||||
|
RegisterDbContext<CloudGamingGameContext>(builder, gameConnectionString, AppDataBaseType.Game);
|
||||||
|
//注册 app配置Db
|
||||||
|
RegisterDbContext<CloudGamingPhoneContext>(builder, phoneConnectionString, AppDataBaseType.App);
|
||||||
|
//注册 扩展Db
|
||||||
|
RegisterDbContext<CloudGamingCBTContext>(builder, extConnectionString, AppDataBaseType.Ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册 DbContext,避免重复代码
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TContext">要注册的 DbContext 类型</typeparam>
|
||||||
|
/// <param name="builder">应用构建器</param>
|
||||||
|
/// <param name="defaultConnectionString">默认连接字符串</param>
|
||||||
|
/// <param name="configKey">AppConfig 中的连接字符串键</param>
|
||||||
|
private static void RegisterDbContext<TContext>(IHostApplicationBuilder builder, string defaultConnectionString, AppDataBaseType configKey) where TContext : DbContext
|
||||||
|
{
|
||||||
|
builder.Services.AddDbContext<TContext>((serviceProvider, options) =>
|
||||||
|
{
|
||||||
|
var appConfig = serviceProvider.GetRequiredService<AppConfig>();
|
||||||
|
string dbConnectionString = appConfig?.GetConnectionString(configKey) ?? defaultConnectionString;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(dbConnectionString))
|
||||||
|
{
|
||||||
|
dbConnectionString = defaultConnectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.UseSqlServer(dbConnectionString);
|
||||||
|
}, ServiceLifetime.Scoped);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 多租户IApplicationBuilder扩展
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IApplicationBuilder UseMultiTenant(this IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
return app.UseMiddleware<MultiTenantTenantMiddleware>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.DataAccess.MultiTenantUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 多租户中间件
|
||||||
|
/// </summary>
|
||||||
|
public class MultiTenantTenantMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
public MultiTenantTenantMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据HttpContext获取并设置当前租户ID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="_serviceProvider"></param>
|
||||||
|
/// <param name="appConfig"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual async Task Invoke(HttpContext context,
|
||||||
|
IServiceProvider _serviceProvider,
|
||||||
|
AppConfig appConfig
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
var host = context.Request.Host.Host;
|
||||||
|
var app = AppConfigurationExtend.GetAppConfig(host);
|
||||||
|
app.ToAppConfig(appConfig);
|
||||||
|
await _next.Invoke(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs
Normal file
15
src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
global using CloudGaming.Code.AppExtend;
|
||||||
|
global using CloudGaming.GameModel.Db.Db_Ext;
|
||||||
|
global using CloudGaming.GameModel.Db.Db_Game;
|
||||||
|
global using CloudGaming.Model.DbSqlServer.Db_Phone;
|
||||||
|
global using CloudGaming.Model.DbSqlServer.Db_User;
|
||||||
|
|
||||||
|
global using HuanMeng.DotNetCore.Base;
|
||||||
|
global using HuanMeng.DotNetCore.MultiTenant;
|
||||||
|
global using HuanMeng.DotNetCore.MultiTenant.Contract;
|
||||||
|
|
||||||
|
global using Microsoft.AspNetCore.Builder;
|
||||||
|
global using Microsoft.EntityFrameworkCore;
|
||||||
|
global using Microsoft.Extensions.Configuration;
|
||||||
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
|
global using Microsoft.Extensions.Hosting;
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CloudGaming.GameModel\CloudGaming.GameModel.csproj" />
|
||||||
|
<ProjectReference Include="..\CloudGaming.Model\CloudGaming.Model.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
10
src/CloudGaming/Model/CloudGaming.DtoModel/GlobalUsings.cs
Normal file
10
src/CloudGaming/Model/CloudGaming.DtoModel/GlobalUsings.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
global using CloudGaming.GameModel.Db.Db_Ext;
|
||||||
|
global using CloudGaming.GameModel.Db.Db_Game;
|
||||||
|
global using CloudGaming.Model.DbSqlServer.Db_Phone;
|
||||||
|
global using CloudGaming.Model.DbSqlServer.Db_User;
|
||||||
|
|
||||||
|
global using HuanMeng.DotNetCore.Base;
|
||||||
|
global using HuanMeng.DotNetCore.MultiTenant;
|
||||||
|
global using HuanMeng.DotNetCore.MultiTenant.Contract;
|
||||||
|
|
||||||
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,18 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
using System;
|
using System.Linq.Expressions;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace HuanMeng.DotNetCore.Base
|
namespace HuanMeng.DotNetCore.Base
|
||||||
{ /// <summary>
|
{
|
||||||
/// 基本数据库操作,需要安装
|
/// <summary>
|
||||||
/// Microsoft.EntityFrameworkCore
|
/// 基本数据库操作,需要安装 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational
|
||||||
/// Microsoft.EntityFrameworkCore.Relational
|
/// </summary>
|
||||||
/// </summary>
|
/// <typeparam name="TDbContext"></typeparam>
|
||||||
/// <typeparam name="TDbContext"></typeparam>
|
|
||||||
public class EfCoreDaoBase<TDbContext> where TDbContext : DbContext
|
public class EfCoreDaoBase<TDbContext> where TDbContext : DbContext
|
||||||
//, new()
|
|
||||||
{
|
{
|
||||||
private TDbContext _context;
|
private TDbContext _context;
|
||||||
public TDbContext context
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
//return _context ?? (_context = new TDbContext());
|
|
||||||
return _context;
|
|
||||||
}
|
|
||||||
//set { _context = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
//public EfCoreDaoBase() { }
|
public TDbContext Context => _context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
|
|
@ -37,8 +20,9 @@ namespace HuanMeng.DotNetCore.Base
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
public EfCoreDaoBase(TDbContext context)
|
public EfCoreDaoBase(TDbContext context)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否手动提交
|
/// 是否手动提交
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -47,149 +31,133 @@ namespace HuanMeng.DotNetCore.Base
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SqlQueryRaw
|
/// SqlQueryRaw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
public async Task<T?> SqlQueryAsync<T>(string sql, params object[] parameters) where T : class
|
||||||
/// <param name="sql"></param>
|
|
||||||
/// <param name="parameters"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public T SqlQuery<T>(string sql, params object[] parameters) where T : class
|
|
||||||
{
|
{
|
||||||
var quiry = context.Database.SqlQueryRaw<T>(sql, parameters);
|
return await Context.Database.SqlQueryRaw<T>(sql, parameters).FirstOrDefaultAsync();
|
||||||
return quiry.FirstOrDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SqlQueryList
|
/// SqlQueryList
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
public async Task<List<T>> SqlQueryListAsync<T>(string sql, params object[] parameters)
|
||||||
/// <param name="sql"></param>
|
|
||||||
/// <param name="parameters"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public List<T> SqlQueryList<T>(string sql, params object[] parameters)
|
|
||||||
{
|
{
|
||||||
var quiry = context.Database.SqlQueryRaw<T>(sql, parameters);
|
return await Context.Database.SqlQueryRaw<T>(sql, parameters).ToListAsync();
|
||||||
return quiry.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ExecuteSql
|
/// ExecuteSql
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sql"></param>
|
public async Task<int> ExecuteSqlAsync(string sql, params object[] parameters)
|
||||||
/// <param name="parameters"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public int ExecuteSql(string sql, params object[] parameters)
|
|
||||||
{
|
{
|
||||||
int r = context.Database.ExecuteSqlRaw(sql, parameters);
|
var result = await Context.Database.ExecuteSqlRawAsync(sql, parameters);
|
||||||
context.SaveChanges();
|
await AutoSaveChangesAsync();
|
||||||
return r;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加实体
|
/// 添加实体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entity"></param>
|
public async Task AddAsync<T>(T entity) where T : class
|
||||||
public void Add<T>(T entity) where T : class
|
|
||||||
{
|
{
|
||||||
context.Set<T>().Add(entity);
|
Context.Set<T>().Add(entity);
|
||||||
if (!IsManualSubmit)
|
await AutoSaveChangesAsync();
|
||||||
context.SaveChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 批量添加实体
|
/// 批量添加实体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
public async Task AddRangeAsync<T>(IEnumerable<T> entities) where T : class
|
||||||
/// <param name="entity"></param>
|
|
||||||
public void AddRange<T>(List<T> entity) where T : class
|
|
||||||
{
|
{
|
||||||
context.Set<T>().AddRange(entity);
|
Context.Set<T>().AddRange(entities);
|
||||||
if (!IsManualSubmit)
|
await AutoSaveChangesAsync();
|
||||||
context.SaveChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 删除某个实体
|
/// 删除某个实体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entity"></param>
|
public async Task DeleteAsync<T>(T entity) where T : class
|
||||||
public void Delete<T>(T entity) where T : class
|
|
||||||
{
|
{
|
||||||
context.Entry<T>(entity).State = EntityState.Deleted; //整条更新
|
Context.Entry(entity).State = EntityState.Deleted;
|
||||||
//context.Set<T>().Remove(entity);
|
await AutoSaveChangesAsync();
|
||||||
if (!IsManualSubmit)
|
|
||||||
context.SaveChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新实体
|
/// 更新实体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entity"></param>
|
public async Task UpdateAsync<T>(T entity) where T : class
|
||||||
public void Update<T>(T entity) where T : class
|
|
||||||
{
|
{
|
||||||
//一般不需要整条更新,按需更新字段即可
|
if (Context.Entry(entity).State == EntityState.Detached)
|
||||||
if (context.Entry<T>(entity).State == EntityState.Detached)
|
|
||||||
{
|
{
|
||||||
context.Set<T>().Attach(entity);
|
Context.Set<T>().Attach(entity);
|
||||||
context.Entry<T>(entity).State = EntityState.Modified; //整条更新
|
Context.Entry(entity).State = EntityState.Modified;
|
||||||
}
|
}
|
||||||
|
await AutoSaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除上下文跟踪
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveTracking<T>(T entity) where T : class
|
||||||
|
{
|
||||||
|
if (Context.Entry(entity).State != EntityState.Detached)
|
||||||
|
{
|
||||||
|
Context.Entry(entity).State = EntityState.Detached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取实体,根据主键
|
||||||
|
/// </summary>
|
||||||
|
public async Task<T?> GetModelAsync<T>(params object[] keyValues) where T : class
|
||||||
|
{
|
||||||
|
return await Context.Set<T>().FindAsync(keyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取实体,根据条件
|
||||||
|
/// </summary>
|
||||||
|
public async Task<T?> GetModelAsync<T>(Expression<Func<T, bool>> where, bool isNoTracking = false) where T : class
|
||||||
|
{
|
||||||
|
var query = Context.Set<T>().AsQueryable();
|
||||||
|
if (isNoTracking)
|
||||||
|
query = query.AsNoTracking();
|
||||||
|
return await query.FirstOrDefaultAsync(where);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取列表数据
|
||||||
|
/// </summary>
|
||||||
|
public IQueryable<T> GetList<T>(Expression<Func<T, bool>> where, bool isNoTracking = false) where T : class
|
||||||
|
{
|
||||||
|
var query = Context.Set<T>().Where(where);
|
||||||
|
return isNoTracking ? query.AsNoTracking() : query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取记录数量
|
||||||
|
/// </summary>
|
||||||
|
public async Task<int> GetCountAsync<T>(Expression<Func<T, bool>> where) where T : class
|
||||||
|
{
|
||||||
|
return await Context.Set<T>().AsNoTracking().CountAsync(where);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否存在记录
|
||||||
|
/// </summary>
|
||||||
|
public async Task<bool> ExistsAsync<T>(Expression<Func<T, bool>> where) where T : class
|
||||||
|
{
|
||||||
|
return await Context.Set<T>().AsNoTracking().AnyAsync(where);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据提交状态决定是否自动保存更改
|
||||||
|
/// </summary>
|
||||||
|
private async Task AutoSaveChangesAsync()
|
||||||
|
{
|
||||||
if (!IsManualSubmit)
|
if (!IsManualSubmit)
|
||||||
context.SaveChanges();
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 清除上下文跟踪(清除缓存) by wyg
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="entity"></param>
|
|
||||||
public void removeTracking<T>(T entity) where T : class
|
|
||||||
{
|
|
||||||
if (context.Entry<T>(entity).State != EntityState.Detached)
|
|
||||||
context.Entry<T>(entity).State = EntityState.Detached;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取实体,从缓存中,根据键值获取
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keyName"></param>
|
|
||||||
/// <param name="keyValue"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public T GetModel<T>(params object[] keyValues) where T : class
|
|
||||||
{
|
|
||||||
return context.Set<T>().Find(keyValues);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 获取实体,根据条件获取,从数据库获取
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="where">筛选条件</param>
|
|
||||||
/// <param name="isNoTracking">默认false会进行缓存并跟踪(可按需update字段),true不需要缓存和跟踪(一般只读查询使用)</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public T GetModel<T>(System.Linq.Expressions.Expression<Func<T, bool>> where, bool isNoTracking = false) where T : class
|
|
||||||
{
|
|
||||||
if (!isNoTracking)
|
|
||||||
return context.Set<T>().FirstOrDefault(where); //可按需update字段
|
|
||||||
return context.Set<T>().AsNoTracking<T>().FirstOrDefault(where); //一般只读查询使用
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取列表数据,返回IQueryable
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="where">筛选条件</param>
|
|
||||||
/// <param name="isNoTracking">默认false会进行缓存并跟踪(可按需update字段),true不需要缓存和跟踪(一般只读查询使用)</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public IQueryable<T> GetList<T>(System.Linq.Expressions.Expression<Func<T, bool>> where, bool isNoTracking = false) where T : class
|
|
||||||
{
|
|
||||||
if (!isNoTracking)
|
|
||||||
return context.Set<T>().Where(where).AsQueryable();
|
|
||||||
return context.Set<T>().Where(where).AsNoTracking<T>().AsQueryable();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCount<T>(System.Linq.Expressions.Expression<Func<T, bool>> where) where T : class
|
|
||||||
{
|
|
||||||
return context.Set<T>().AsNoTracking<T>().Count(where);
|
|
||||||
}
|
|
||||||
public bool Exists<T>(System.Linq.Expressions.Expression<Func<T, bool>> where) where T : class
|
|
||||||
{
|
|
||||||
//return context.Set<T>().AsNoTracking<T>().Any(where);
|
|
||||||
return context.Set<T>().AsNoTracking<T>().Count(where) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -20,6 +22,7 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.8.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
using HuanMeng.DotNetCore.JwtInfrastructure.Interface;
|
||||||
|
using HuanMeng.DotNetCore.JwtInfrastructure;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HuanMeng.DotNetCore.JwtInfrastructure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// jwt,不保存
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwtTokenConfig"></param>
|
||||||
|
public class JwtManager(JwtTokenConfig jwtTokenConfig) : IJwtAuthManager
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 后面可以存储在数据库或分布式缓存中
|
||||||
|
/// </summary>
|
||||||
|
private readonly ConcurrentDictionary<string, JwtRefreshToken> _usersRefreshTokens = new();
|
||||||
|
/// <summary>
|
||||||
|
/// 获取加密字段
|
||||||
|
/// </summary>
|
||||||
|
private readonly byte[] _secret = Encoding.UTF8.GetBytes(jwtTokenConfig.Secret);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除过期token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="now"></param>
|
||||||
|
public void RemoveExpiredRefreshTokens(DateTime now)
|
||||||
|
{
|
||||||
|
var expiredTokens = _usersRefreshTokens.Where(x => x.Value.ExpireAt < now).ToList();
|
||||||
|
foreach (var expiredToken in expiredTokens)
|
||||||
|
{
|
||||||
|
_usersRefreshTokens.TryRemove(expiredToken.Key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据用户名删除token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userName"></param>
|
||||||
|
public void RemoveRefreshTokenByUserName(string userName)
|
||||||
|
{
|
||||||
|
var refreshTokens = _usersRefreshTokens.Where(x => x.Value.UserName == userName).ToList();
|
||||||
|
foreach (var refreshToken in refreshTokens)
|
||||||
|
{
|
||||||
|
_usersRefreshTokens.TryRemove(refreshToken.Key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="username">用户名</param>
|
||||||
|
/// <param name="claims">用户项</param>
|
||||||
|
/// <param name="now">过期时间</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public JwtAuthResult GenerateTokens(string username, Claim[] claims, DateTime now)
|
||||||
|
{
|
||||||
|
var shouldAddAudienceClaim = string.IsNullOrWhiteSpace(claims.FirstOrDefault(x => x.Type == JwtRegisteredClaimNames.Aud)?.Value);
|
||||||
|
//创建token
|
||||||
|
var jwtToken = new JwtSecurityToken(
|
||||||
|
jwtTokenConfig.Issuer,
|
||||||
|
shouldAddAudienceClaim ? jwtTokenConfig.Audience : string.Empty,
|
||||||
|
claims,
|
||||||
|
expires: now.AddMinutes(jwtTokenConfig.AccessTokenExpiration),
|
||||||
|
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(_secret), SecurityAlgorithms.HmacSha256Signature));
|
||||||
|
var accessToken = new JwtSecurityTokenHandler().WriteToken(jwtToken);
|
||||||
|
|
||||||
|
//创建刷新token
|
||||||
|
var refreshToken = new JwtRefreshToken
|
||||||
|
{
|
||||||
|
UserName = username,
|
||||||
|
TokenString = GenerateRefreshTokenString(),
|
||||||
|
ExpireAt = now.AddMinutes(jwtTokenConfig.RefreshTokenExpiration)
|
||||||
|
};
|
||||||
|
//_usersRefreshTokens.AddOrUpdate(refreshToken.TokenString, refreshToken, (_, _) => refreshToken);
|
||||||
|
|
||||||
|
return new JwtAuthResult
|
||||||
|
{
|
||||||
|
AccessToken = accessToken,
|
||||||
|
RefreshToken = refreshToken
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="refreshToken"></param>
|
||||||
|
/// <param name="accessToken"></param>
|
||||||
|
/// <param name="now"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="SecurityTokenException"></exception>
|
||||||
|
public JwtAuthResult Refresh(string refreshToken, string accessToken, DateTime now)
|
||||||
|
{
|
||||||
|
var (principal, jwtToken) = DecodeJwtToken(accessToken);
|
||||||
|
if (jwtToken == null || !jwtToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256Signature))
|
||||||
|
{
|
||||||
|
throw new SecurityTokenException("无效的token");
|
||||||
|
}
|
||||||
|
|
||||||
|
var userName = principal.Identity?.Name;
|
||||||
|
if (!_usersRefreshTokens.TryGetValue(refreshToken, out var existingRefreshToken))
|
||||||
|
{
|
||||||
|
throw new SecurityTokenException("token已失效");
|
||||||
|
}
|
||||||
|
if (existingRefreshToken.UserName != userName || existingRefreshToken.ExpireAt < now)
|
||||||
|
{
|
||||||
|
throw new SecurityTokenException("token不匹配");
|
||||||
|
}
|
||||||
|
//创建新的token
|
||||||
|
return GenerateTokens(userName, principal.Claims.ToArray(), now);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 解析token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="SecurityTokenException"></exception>
|
||||||
|
public (ClaimsPrincipal, JwtSecurityToken?) DecodeJwtToken(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
|
{
|
||||||
|
throw new SecurityTokenException("token不能为空");
|
||||||
|
}
|
||||||
|
var principal = new JwtSecurityTokenHandler()
|
||||||
|
.ValidateToken(token,
|
||||||
|
new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidIssuer = jwtTokenConfig.Issuer,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(_secret),
|
||||||
|
ValidAudience = jwtTokenConfig.Audience,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ClockSkew = TimeSpan.FromMinutes(5)
|
||||||
|
},
|
||||||
|
out var validatedToken);
|
||||||
|
return (principal, validatedToken as JwtSecurityToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取刷新的token
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string GenerateRefreshTokenString()
|
||||||
|
{
|
||||||
|
var randomNumber = new byte[32];
|
||||||
|
using var randomNumberGenerator = RandomNumberGenerator.Create();
|
||||||
|
randomNumberGenerator.GetBytes(randomNumber);
|
||||||
|
return Convert.ToBase64String(randomNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IImmutableDictionary<string, JwtRefreshToken> UsersRefreshTokensReadOnlyDictionary => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,9 +20,5 @@ namespace HuanMeng.DotNetCore.MultiTenant.Contract
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string? Name { get; set; }
|
string? Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 数据库连接字符串
|
|
||||||
/// </summary>
|
|
||||||
string? ConnectionString { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,6 @@ namespace HuanMeng.DotNetCore.MultiTenant
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 租户数据库连接字符串
|
|
||||||
/// </summary>
|
|
||||||
public string? ConnectionString { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HuanMeng.DotNetCore.SwaggerUtile
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义参数过滤器
|
||||||
|
/// </summary>
|
||||||
|
public class LowercaseParameterFilter : IParameterFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
|
||||||
|
{
|
||||||
|
// 将参数名称改为小写开头
|
||||||
|
parameter.Name = Char.ToLower(parameter.Name[0]) + parameter.Name.Substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义参数过滤器
|
||||||
|
/// </summary>
|
||||||
|
public class LowercaseRequestFilter : IRequestBodyFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (requestBody.Content != null)
|
||||||
|
{
|
||||||
|
foreach (var mediaType in requestBody.Content.Values)
|
||||||
|
{
|
||||||
|
if (mediaType.Schema?.Properties != null)
|
||||||
|
{
|
||||||
|
var propertiesToRename = new Dictionary<string, OpenApiSchema>(mediaType.Schema.Properties);
|
||||||
|
// 清空旧的属性
|
||||||
|
mediaType.Schema.Properties.Clear();
|
||||||
|
|
||||||
|
foreach (var property in propertiesToRename)
|
||||||
|
{
|
||||||
|
// 创建新的属性,并将名称改为小写开头
|
||||||
|
var newPropertyName = Char.ToLower(property.Key[0]) + property.Key.Substring(1);
|
||||||
|
mediaType.Schema.Properties.Add(newPropertyName, property.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,22 +14,22 @@ namespace HuanMeng.DotNetCore.Utility.AssemblyHelper
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置程序集的版本。
|
/// 获取或设置程序集的版本。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Version { get; set; }
|
public string? Version { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置程序集的文件版本。
|
/// 获取或设置程序集的文件版本。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string FileVersion { get; set; }
|
public string? FileVersion { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置程序集版本。
|
/// 获取或设置程序集版本。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string AssemblyVersion { get; set; }
|
public string? AssemblyVersion { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置程序集的信息性版本。
|
/// 获取或设置程序集的信息性版本。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string InformationalVersion { get; set; }
|
public string? InformationalVersion { get; set; }
|
||||||
|
|
||||||
///// <summary>
|
///// <summary>
|
||||||
///// 获取或设置与程序集关联的公司名称。
|
///// 获取或设置与程序集关联的公司名称。
|
||||||
|
|
@ -44,12 +44,12 @@ namespace HuanMeng.DotNetCore.Utility.AssemblyHelper
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置与程序集关联的版权信息。
|
/// 获取或设置与程序集关联的版权信息。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Copyright { get; set; }
|
public string? Copyright { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置程序集的描述信息。
|
/// 获取或设置程序集的描述信息。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Description { get; set; }
|
public string? Description { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace HuanMeng.DotNetCore.Utility
|
namespace HuanMeng.DotNetCore.Utility
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
public static class HttpContextExtensions
|
public static class HttpContextExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ namespace HuanMeng.DotNetCore.WeChat
|
||||||
|
|
||||||
public static class ErrorCode
|
public static class ErrorCode
|
||||||
{
|
{
|
||||||
|
|
||||||
public const int OK = 0;
|
public const int OK = 0;
|
||||||
public const int IllegalAesKey = -41001;
|
public const int IllegalAesKey = -41001;
|
||||||
public const int IllegalIv = -41002;
|
public const int IllegalIv = -41002;
|
||||||
|
|
@ -106,14 +107,15 @@ namespace HuanMeng.DotNetCore.WeChat
|
||||||
|
|
||||||
public class WXBizDataCryptModel
|
public class WXBizDataCryptModel
|
||||||
{
|
{
|
||||||
|
public WXBizDataCryptModel() { }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string EncryptedData;
|
public string? EncryptedData;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Iv;
|
public string? Iv;
|
||||||
|
|
||||||
|
|
||||||
public int UserId { get; set; }
|
public int UserId { get; set; }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user