feat:框架基础设施搭建
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Yi.Framework.Infrastructure.CurrentUsers;
|
||||
using Yi.Framework.Infrastructure.CurrentUsers.Accessor;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.AspNetCore
|
||||
{
|
||||
public static class CurrentUserAddExtensions
|
||||
{
|
||||
public static IServiceCollection AddCurrentUserServer(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<ICurrentPrincipalAccessor, HttpContextCurrentPrincipalAccessor>();
|
||||
return services.AddTransient<ICurrentUser, CurrentUser>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Yi.Framework.Infrastructure
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.Const
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义公共文件常量
|
||||
/// </summary>
|
||||
public class PathConst
|
||||
{
|
||||
public const string wwwroot = "wwwroot";
|
||||
public const string DataTemplate = "_DataTemplate";
|
||||
public const string DataExport = "_DataExport";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.Const
|
||||
{
|
||||
public class TokenTypeConst
|
||||
{
|
||||
public const string Id = nameof(Id);
|
||||
|
||||
public const string UserName = nameof(UserName);
|
||||
|
||||
public const string TenantId = nameof(TenantId);
|
||||
|
||||
public const string Email = nameof(Email);
|
||||
|
||||
public const string PhoneNumber = nameof(PhoneNumber);
|
||||
|
||||
public const string Roles = nameof(Roles);
|
||||
|
||||
public const string Permission = nameof(Permission);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Security.Claims;
|
||||
using Yi.Framework.Infrastructure.Utils;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.CurrentUsers.Accessor
|
||||
{
|
||||
public abstract class CurrentPrincipalAccessorBase : ICurrentPrincipalAccessor
|
||||
{
|
||||
public ClaimsPrincipal Principal => _currentPrincipal.Value ?? GetClaimsPrincipal();
|
||||
|
||||
private readonly AsyncLocal<ClaimsPrincipal> _currentPrincipal = new AsyncLocal<ClaimsPrincipal>();
|
||||
|
||||
protected abstract ClaimsPrincipal GetClaimsPrincipal();
|
||||
|
||||
public virtual IDisposable Change(ClaimsPrincipal principal)
|
||||
{
|
||||
return SetCurrent(principal);
|
||||
}
|
||||
|
||||
private IDisposable SetCurrent(ClaimsPrincipal principal)
|
||||
{
|
||||
var parent = Principal;
|
||||
_currentPrincipal.Value = principal;
|
||||
return new DisposeAction<ValueTuple<AsyncLocal<ClaimsPrincipal>, ClaimsPrincipal>>(static (state) =>
|
||||
{
|
||||
var (currentPrincipal, parent) = state;
|
||||
currentPrincipal.Value = parent;
|
||||
}, (_currentPrincipal, parent));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.CurrentUsers.Accessor
|
||||
{
|
||||
public class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
protected override ClaimsPrincipal GetClaimsPrincipal()
|
||||
{
|
||||
return _httpContextAccessor.HttpContext?.User ?? base.GetClaimsPrincipal();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.CurrentUsers.Accessor
|
||||
{
|
||||
public interface ICurrentPrincipalAccessor
|
||||
{
|
||||
ClaimsPrincipal Principal { get; }
|
||||
IDisposable Change(ClaimsPrincipal principal);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.CurrentUsers.Accessor
|
||||
{
|
||||
public class StaticPrincipalAccessor : CurrentPrincipalAccessorBase
|
||||
{
|
||||
public static ClaimsPrincipal ClaimsPrincipal { get; set; }
|
||||
protected override ClaimsPrincipal GetClaimsPrincipal()
|
||||
{
|
||||
return ClaimsPrincipal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.CurrentUsers.Accessor
|
||||
{
|
||||
public class ThreadCurrentPrincipalAccessor : CurrentPrincipalAccessorBase
|
||||
{
|
||||
protected override ClaimsPrincipal GetClaimsPrincipal()
|
||||
{
|
||||
return Thread.CurrentPrincipal as ClaimsPrincipal;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Infrastructure.Const;
|
||||
using Yi.Framework.Infrastructure.CurrentUsers.Accessor;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.CurrentUsers
|
||||
{
|
||||
public class CurrentUser : ICurrentUser
|
||||
{
|
||||
private readonly ICurrentPrincipalAccessor _principalAccessor;
|
||||
public CurrentUser(ICurrentPrincipalAccessor principalAccessor)
|
||||
{
|
||||
_principalAccessor = principalAccessor;
|
||||
}
|
||||
public bool IsAuthenticated => Id != 0;
|
||||
|
||||
public long Id => FindUserId();
|
||||
|
||||
public string UserName => this.FindClaimValue(TokenTypeConst.UserName);
|
||||
|
||||
/// <summary>
|
||||
/// 暂时为默认值
|
||||
/// </summary>
|
||||
public Guid TenantId { get; set; } = Guid.Empty;
|
||||
|
||||
public string Email => FindClaimValue(TokenTypeConst.Email);
|
||||
|
||||
public bool EmailVerified => false;
|
||||
|
||||
public string PhoneNumber => FindClaimValue(TokenTypeConst.PhoneNumber);
|
||||
|
||||
public bool PhoneNumberVerified => false;
|
||||
|
||||
public string[]? Roles => this.FindClaims(TokenTypeConst.Roles).Select(c => c.Value).Distinct().ToArray();
|
||||
|
||||
public string[]? Permission => this.FindClaims(TokenTypeConst.Permission).Select(c => c.Value).Distinct().ToArray();
|
||||
|
||||
public virtual Claim FindClaim(string claimType)
|
||||
{
|
||||
return _principalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == claimType);
|
||||
}
|
||||
|
||||
public virtual Claim[] FindClaims(string claimType)
|
||||
{
|
||||
return _principalAccessor.Principal?.Claims.Where(c => c.Type == claimType).ToArray() ?? new Claim[0];
|
||||
}
|
||||
|
||||
public virtual Claim[] GetAllClaims()
|
||||
{
|
||||
return _principalAccessor.Principal?.Claims.ToArray() ?? new Claim[0];
|
||||
}
|
||||
|
||||
public string FindClaimValue(string claimType)
|
||||
{
|
||||
return FindClaim(claimType)?.Value;
|
||||
}
|
||||
|
||||
|
||||
public long FindUserId()
|
||||
{
|
||||
var userIdOrNull = _principalAccessor.Principal?.Claims?.FirstOrDefault(c => c.Type == TokenTypeConst.Id);
|
||||
if (userIdOrNull == null || string.IsNullOrWhiteSpace(userIdOrNull.Value))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (long.TryParse(userIdOrNull.Value, out long userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.CurrentUsers
|
||||
{
|
||||
public interface ICurrentUser
|
||||
{
|
||||
public bool IsAuthenticated { get; }
|
||||
public long Id { get; }
|
||||
|
||||
public string UserName { get; }
|
||||
|
||||
public Guid TenantId { get; }
|
||||
|
||||
public string Email { get; }
|
||||
|
||||
public bool EmailVerified { get; }
|
||||
|
||||
public string PhoneNumber { get; }
|
||||
|
||||
public bool PhoneNumberVerified { get; }
|
||||
|
||||
public string[]? Roles { get; }
|
||||
|
||||
public string[]? Permission { get; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.Sqlsugar
|
||||
{
|
||||
public class DbConnOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接字符串,必填
|
||||
/// </summary>
|
||||
public string? Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据库类型
|
||||
/// </summary>
|
||||
public DbType? DbType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开启种子数据
|
||||
/// </summary>
|
||||
public bool EnabledDbSeed { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 开启读写分离
|
||||
/// </summary>
|
||||
public bool EnabledReadWrite { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 开启codefirst
|
||||
/// </summary>
|
||||
public bool EnabledCodeFirst { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 实体程序集
|
||||
/// </summary>
|
||||
public List<string>? EntityAssembly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 读写分离
|
||||
/// </summary>
|
||||
public List<string>? ReadUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Infrastructure.CurrentUsers;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.Sqlsugar
|
||||
{
|
||||
public class SqlSugarDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// SqlSugar 客户端
|
||||
/// </summary>
|
||||
public ISqlSugarClient SqlSugarClient { get; set; }
|
||||
|
||||
protected ICurrentUser _currentUser;
|
||||
|
||||
protected ILogger<SqlSugarDbContext> _logger;
|
||||
|
||||
protected IOptions<DbConnOptions> _options;
|
||||
|
||||
public SqlSugarDbContext(IOptions<DbConnOptions> options, ICurrentUser currentUser, ILogger<SqlSugarDbContext> logger)
|
||||
{
|
||||
_currentUser = currentUser;
|
||||
_logger = logger;
|
||||
_options = options;
|
||||
var dbConnOptions = options.Value;
|
||||
#region 组装options
|
||||
if (dbConnOptions.DbType is null)
|
||||
{
|
||||
throw new ArgumentException(SqlsugarConst.DbType配置为空);
|
||||
}
|
||||
var slavaConFig = new List<SlaveConnectionConfig>();
|
||||
if (dbConnOptions.EnabledReadWrite)
|
||||
{
|
||||
if (dbConnOptions.ReadUrl is null)
|
||||
{
|
||||
throw new ArgumentException(SqlsugarConst.读写分离为空);
|
||||
}
|
||||
|
||||
var readCon = dbConnOptions.ReadUrl;
|
||||
|
||||
readCon.ForEach(s =>
|
||||
{
|
||||
//如果是动态saas分库,这里的连接串都不能写死,需要动态添加,这里只配置共享库的连接
|
||||
slavaConFig.Add(new SlaveConnectionConfig() { ConnectionString = s });
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
SqlSugarClient = new SqlSugarScope(new ConnectionConfig()
|
||||
{
|
||||
//准备添加分表分库
|
||||
DbType = dbConnOptions.DbType ?? DbType.Sqlite,
|
||||
ConnectionString = dbConnOptions.Url,
|
||||
IsAutoCloseConnection = true,
|
||||
MoreSettings = new ConnMoreSettings()
|
||||
{
|
||||
DisableNvarchar = true
|
||||
},
|
||||
SlaveConnectionConfigs = slavaConFig,
|
||||
//设置codefirst非空值判断
|
||||
ConfigureExternalServices = new ConfigureExternalServices
|
||||
{
|
||||
EntityService = (c, p) =>
|
||||
{
|
||||
//高版C#写法 支持string?和string
|
||||
if (new NullabilityInfoContext()
|
||||
.Create(c).WriteState is NullabilityState.Nullable)
|
||||
{
|
||||
p.IsNullable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
db =>
|
||||
{
|
||||
|
||||
db.Aop.DataExecuting = (oldValue, entityInfo) =>
|
||||
{
|
||||
|
||||
//switch (entityInfo.OperationType)
|
||||
//{
|
||||
// case DataFilterType.UpdateByObject:
|
||||
|
||||
// if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModificationTime)))
|
||||
// {
|
||||
// entityInfo.SetValue(DateTime.Now);
|
||||
// }
|
||||
// if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModifierId)))
|
||||
// {
|
||||
// if (_currentUser != null)
|
||||
// {
|
||||
// entityInfo.SetValue(_currentUser.Id);
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// case DataFilterType.InsertByObject:
|
||||
// if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreationTime)))
|
||||
// {
|
||||
// entityInfo.SetValue(DateTime.Now);
|
||||
// }
|
||||
// if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreatorId)))
|
||||
// {
|
||||
// if (_currentUser != null)
|
||||
// {
|
||||
// entityInfo.SetValue(_currentUser.Id);
|
||||
// }
|
||||
// }
|
||||
|
||||
// //插入时,需要租户id,先预留
|
||||
// if (entityInfo.PropertyName.Equals(nameof(IMultiTenant.TenantId)))
|
||||
// {
|
||||
// //if (this.CurrentTenant is not null)
|
||||
// //{
|
||||
// // entityInfo.SetValue(this.CurrentTenant.Id);
|
||||
// //}
|
||||
// }
|
||||
// break;
|
||||
//}
|
||||
};
|
||||
db.Aop.OnLogExecuting = (s, p) =>
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
//sb.Append("执行SQL:" + s.ToString());
|
||||
//foreach (var i in p)
|
||||
//{
|
||||
// sb.Append($"\r\n参数:{i.ParameterName},参数值:{i.Value}");
|
||||
//}
|
||||
sb.Append($"\r\n 完整SQL:{UtilMethods.GetSqlString(DbType.MySql, s, p)}");
|
||||
logger?.LogDebug(sb.ToString());
|
||||
};
|
||||
//扩展
|
||||
OnSqlSugarClientConfig(db);
|
||||
});
|
||||
}
|
||||
|
||||
//上下文对象扩展
|
||||
protected virtual void OnSqlSugarClientConfig(ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.Sqlsugar
|
||||
{
|
||||
public class SqlsugarConst
|
||||
{
|
||||
public const string 读写分离为空 = "开启读写分离后,读库连接不能为空";
|
||||
|
||||
public const string DbType配置为空 = "DbType配置为空,必须选择一个数据库类型";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.Sqlsugar
|
||||
{
|
||||
/// <summary>
|
||||
/// 这一块,需要做成上下文对象,会进行重构
|
||||
/// </summary>
|
||||
public static class SqlsugarExtensions
|
||||
{
|
||||
//使用上下文对象
|
||||
public static void AddDbSqlsugarContextServer(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton(x => x.GetRequiredService<SqlSugarDbContext>().SqlSugarClient);
|
||||
services.AddSingleton<SqlSugarDbContext>();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
26
Yi.Furion.Rbac/Yi.Framework.Infrastructure/Startup.cs
Normal file
26
Yi.Furion.Rbac/Yi.Framework.Infrastructure/Startup.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Furion;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Yi.Framework.Infrastructure.AspNetCore;
|
||||
using Yi.Framework.Infrastructure.Sqlsugar;
|
||||
|
||||
namespace Yi.Framework.Infrastructure;
|
||||
|
||||
public class Startup : AppStartup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddCurrentUserServer();
|
||||
|
||||
services.Configure<DbConnOptions>(App.Configuration.GetSection("DbConnOptions"));
|
||||
|
||||
services.AddDbSqlsugarContextServer();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Yi.Framework.Infrastructure.Utils
|
||||
{
|
||||
public class DisposeAction<T> : IDisposable
|
||||
{
|
||||
private readonly Action<T> _action;
|
||||
|
||||
private readonly T _parameter;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="DisposeAction"/> object.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to be executed when this object is disposed.</param>
|
||||
/// <param name="parameter">The parameter of the action.</param>
|
||||
public DisposeAction(Action<T> action, T parameter)
|
||||
{
|
||||
_action = action;
|
||||
_parameter = parameter;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_action(_parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Sqlsugar\" />
|
||||
<ProjectReference Include="..\Yi.Furion.Rbac.Core\Yi.Furion.Rbac.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user