fix: 🐛 操作日志不显示userName (#141)

[skip ci]

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
2024-06-12 21:47:08 +08:00
committed by GitHub
parent a3ab97019d
commit 705d027da4
19 changed files with 216 additions and 222 deletions

View File

@ -10,11 +10,6 @@ public interface IFieldCreatedClient
/// </summary>
int? CreatedClientIp { get; init; }
/// <summary>
/// 创建者来源地址
/// </summary>
string CreatedReferer { get; init; }
/// <summary>
/// 创建者客户端用户代理
/// </summary>

View File

@ -1,5 +1,16 @@
using NetAdmin.Domain.Attributes;
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <inheritdoc />
public abstract record SimpleEntity : SimpleEntity<long>
{
/// <inheritdoc cref="EntityBase{T}.Id" />
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
[Snowflake]
public override long Id { get; init; }
}
/// <summary>
/// 简单实体
/// </summary>

View File

@ -31,7 +31,7 @@ public record Sys_JobRecord : LiteImmutableEntity
/// </summary>
[Column]
[JsonIgnore]
public virtual int HttpStatusCode { get; init; }
public int HttpStatusCode { get; init; }
/// <summary>
/// 拥有者信息

View File

@ -8,9 +8,10 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// </summary>
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiId), nameof(ApiId), false)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(UserId), nameof(UserId), false)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))]
public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldCreatedClient
{
/// <summary>
/// 接口
@ -26,23 +27,17 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
[JsonIgnore]
public virtual string ApiId { get; init; }
/// <summary>
/// 创建者客户端IP
/// </summary>
/// <inheritdoc />
[Column(Position = -1)]
[JsonIgnore]
public int? CreatedClientIp { get; init; }
/// <summary>
/// 创建者来源地址
/// </summary>
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
/// <inheritdoc />
[Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)]
[JsonIgnore]
public string CreatedReferer { get; init; }
public virtual DateTime CreatedTime { get; init; }
/// <summary>
/// 创建者客户端用户代理
/// </summary>
/// <inheritdoc />
#if DBTYPE_SQLSERVER
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_1022)]
#else
@ -76,17 +71,6 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
[JsonIgnore]
public virtual string Exception { get; init; }
/// <summary>
/// 附加数据
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[JsonIgnore]
public virtual string ExtraData { get; init; }
/// <summary>
/// HTTP状态码
/// </summary>
@ -101,13 +85,6 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
[JsonIgnore]
public virtual string Method { get; init; }
/// <summary>
/// 来源地址
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string ReferUrl { get; init; }
/// <summary>
/// 请求内容
/// </summary>
@ -179,4 +156,18 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
[Column]
[JsonIgnore]
public virtual int? ServerIp { get; init; }
/// <summary>
/// 用户
/// </summary>
[JsonIgnore]
[Navigate(nameof(UserId))]
public Sys_User User { get; init; }
/// <summary>
/// 用户编号
/// </summary>
[Column]
[JsonIgnore]
public virtual long? UserId { get; init; }
}

View File

@ -1,5 +1,6 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Dto.Sys.RequestLog;
@ -13,6 +14,11 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
/// </summary>
public new string CreatedClientIp => base.CreatedClientIp?.ToIpV4();
/// <summary>
/// 登录名
/// </summary>
public string LoginName => RequestBody?.ToObject<LoginByPwdReq>()?.Account;
/// <summary>
/// 操作系统
/// </summary>
@ -35,10 +41,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string CreatedUserAgent { get; init; }
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserName" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string CreatedUserName { get; init; }
/// <inheritdoc cref="Sys_RequestLog.Duration" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Duration { get; init; }
@ -51,10 +53,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Exception { get; init; }
/// <inheritdoc cref="Sys_RequestLog.ExtraData" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ExtraData { get; init; }
/// <inheritdoc cref="Sys_RequestLog.HttpStatusCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override int HttpStatusCode { get; init; }
@ -63,10 +61,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Method { get; init; }
/// <inheritdoc cref="Sys_RequestLog.ReferUrl" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ReferUrl { get; init; }
/// <inheritdoc cref="Sys_RequestLog.RequestBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; }
@ -99,6 +93,13 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override int? ServerIp { get; init; }
/// <inheritdoc cref="Sys_RequestLog.User" />
public new QueryUserRsp User { get; init; }
/// <inheritdoc cref="Sys_RequestLog.UserId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? UserId { get; init; }
/// <inheritdoc />
public void Register(TypeAdapterConfig config)
{

View File

@ -7,17 +7,10 @@ namespace NetAdmin.Host.Utils;
/// <summary>
/// 请求日志记录器
/// </summary>
public sealed class RequestLogger(
ILogger<RequestLogger> logger
, IOptions<SpecificationDocumentSettingsOptions> specificationDocumentSettingsOptions
, IEventPublisher eventPublisher) : ISingleton
public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher eventPublisher) : ISingleton
{
private static readonly string[] _textContentTypes = ["text", "json", "xml", "urlencoded"];
private readonly int _tokenPrefixLength
= specificationDocumentSettingsOptions?.Value.SecurityDefinitions?[0]?.Scheme?.Length + 1 ??
0; // eg. "Bearer ";
/// <summary>
/// 生成审计数据
/// </summary>
@ -30,7 +23,6 @@ public sealed class RequestLogger(
var auditData = new CreateRequestLogReq {
Duration = duration
, Method = context.Request.Method
, ReferUrl = context.Request.GetRefererUrlAddress()
, RequestContentType = context.Request.ContentType
, RequestBody = Array.Exists( //
_textContentTypes
@ -50,8 +42,7 @@ public sealed class RequestLogger(
, HttpStatusCode = context.Response.StatusCode
, ErrorCode = errorCode
, Exception = exception?.Error.ToString()
, CreatedUserId = associatedUser?.UserId
, CreatedUserName = associatedUser?.UserName
, UserId = associatedUser?.UserId
, CreatedUserAgent = context.Request.Headers.UserAgent.ToString()
, CreatedClientIp = context.GetRealIpAddress()
?.MapToIPv4()
@ -77,8 +68,9 @@ public sealed class RequestLogger(
ContextUserToken userToken = null;
try {
var jsonWebToken = JWTEncryption.ReadJwtToken(token[_tokenPrefixLength..]);
var claim = jsonWebToken?.Claims.FirstOrDefault(y => y.Type == nameof(ContextUserToken));
var jsonWebToken
= JWTEncryption.ReadJwtToken(token.TrimStart($"{Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA} "));
var claim = jsonWebToken?.Claims.FirstOrDefault(y => y.Type == nameof(ContextUserToken));
userToken = claim?.Value.ToObject<ContextUserToken>();
}
catch (Exception ex) {

View File

@ -63,13 +63,6 @@ public sealed class SqlAuditor : ISingleton
}
}
private static void SetCreatedReferer(AuditValueEventArgs e)
{
if (e.Value is null or "") {
e.Value = App.HttpContext?.Request.GetRefererUrlAddress();
}
}
private static void SetCreatedTime(AuditValueEventArgs e)
{
if (e.Value == null || (e.Value is DateTime val && val == default)) {
@ -119,9 +112,6 @@ public sealed class SqlAuditor : ISingleton
case nameof(IFieldCreatedClient.CreatedUserAgent):
SetCreatedUserAgent(e);
break;
case nameof(IFieldCreatedClient.CreatedReferer):
SetCreatedReferer(e);
break;
default:
return;
}

View File

@ -67,6 +67,7 @@ public static class Chars
public const string FLG_HTTP_METHOD_POST = "POST";
public const string FLG_HTTP_METHOD_PUT = "PUT";
public const string FLG_HTTP_METHOD_TRACE = "TRACE";
public const string FLG_PATH_API_SYS_USER_LOGIN_BY_PWD = "api/sys/user/login.by.pwd";
public const string FLG_PATH_PREFIX_HEALTH_CHECK = "probe/health.check";
public const string FLG_RANDOM_UNAME_PWD = "VcXlp7WY";
public const string FLG_REDIS_INSTANCE_DATA_CACHE = "DataCache";

View File

@ -9,9 +9,9 @@
<PackageReference Include="Cronos" Version="0.8.4"/>
<PackageReference Include="FreeSql.DbContext.NS" Version="3.2.821-ns1"/>
<PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.821-ns1"/>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.3"/>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.3-ns1"/>
<PackageReference Include="Furion.Pure.NS" Version="4.9.3-ns1"/>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.4"/>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.4-ns1"/>
<PackageReference Include="Furion.Pure.NS" Version="4.9.4-ns1"/>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.4.24267.6"/>
<PackageReference Include="Minio" Version="6.0.2"/>
<PackageReference Include="NSExt" Version="2.1.0"/>

View File

@ -12,6 +12,8 @@ namespace NetAdmin.SysComponent.Application.Services.Sys;
public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo) //
: RepositoryService<Sys_RequestLog, long, IRequestLogService>(rpo), IRequestLogService
{
private static readonly Regex _regex = new(Chars.RGXL_IP_V4);
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
@ -134,26 +136,45 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
public async Task<PagedQueryRsp<QueryRequestLogRsp>> PagedQueryAsync(PagedQueryReq<QueryRequestLogReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Count(out var total)
.ToListAsync(a => new {
a.ApiId
, ApiSummary = a.Api.Summary
, a.ExtraData
, a.CreatedClientIp
, a.CreatedTime
, a.CreatedUserName
, a.Duration
, a.Method
, a.CreatedUserAgent
, a.HttpStatusCode
, a.Id
})
.ConfigureAwait(false);
var select = QueryInternal(req)
.Page(req.Page, req.PageSize)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Count(out var total);
object list = req.DynamicFilter?.Filters?.Exists(
x => nameof(QueryRequestLogReq.ApiId).Equals(x.Field, StringComparison.OrdinalIgnoreCase) &&
Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Equals( //
x.Value.ToString(), StringComparison.OrdinalIgnoreCase)) ?? false
? await select.ToListAsync(a => new {
a.ApiId
, ApiSummary = a.Api.Summary
, a.CreatedClientIp
, a.CreatedTime
, a.Duration
, a.Method
, a.CreatedUserAgent
, a.HttpStatusCode
, a.Id
, a.UserId
, a.User
, a.RequestBody
})
.ConfigureAwait(false)
: await select.ToListAsync(a => new {
a.ApiId
, ApiSummary = a.Api.Summary
, a.CreatedClientIp
, a.CreatedTime
, a.Duration
, a.Method
, a.CreatedUserAgent
, a.HttpStatusCode
, a.Id
, a.UserId
, a.User
})
.ConfigureAwait(false);
return new PagedQueryRsp<QueryRequestLogRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryRequestLogRsp>>());
@ -175,7 +196,18 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
private ISelect<Sys_RequestLog> QueryInternal(QueryReq<QueryRequestLogReq> req)
{
var ret = Rpo.Select.Include(a => a.Api).WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
var ret = Rpo.Select.Include(a => a.Api).Include(a => a.User).WhereDynamicFilter(req.DynamicFilter);
if (req.Filter?.Id is not 0) {
ret = ret.WhereDynamic(req.Filter);
}
if (req.Keywords?.Length > 0) {
ret = _regex.IsMatch(req.Keywords)
? ret.Where(a => a.CreatedClientIp == req.Keywords.IpV4ToInt32())
: ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.UserId == req.Keywords.Int64Try(0) ||
a.User.UserName == req.Keywords || a.RequestBody.Contains(req.Keywords));
}
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -1,5 +1,3 @@
using NetAdmin.Domain.Dto.Sys.RequestLog;
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.Domain.Events.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
@ -27,22 +25,7 @@ public sealed class OperationLogger : IEventSubscriber
return;
}
CreateRequestLogReq logReq = null;
// 登录日志特殊处理
if (operationEvent.Data.ApiId.Equals("api/sys/user/login.by.pwd", StringComparison.OrdinalIgnoreCase)) {
try {
var loginReq = operationEvent.Data.RequestBody.ToObject<LoginByPwdReq>();
logReq = operationEvent.Data with { ExtraData = loginReq.Account };
}
catch {
// ignored
}
}
logReq ??= operationEvent.Data;
var logService = App.GetService<IRequestLogService>();
logReq.TruncateStrings();
_ = await logService.CreateAsync(logReq).ConfigureAwait(false);
operationEvent.Data.TruncateStrings();
_ = await App.GetService<IRequestLogService>().CreateAsync(operationEvent.Data).ConfigureAwait(false);
}
}

View File

@ -15,10 +15,8 @@ public abstract class WebApiTestBase<T>(WebApplicationFactory<T> factory, ITestO
: IClassFixture<WebApplicationFactory<T>>
where T : AppStartup
{
private const string _ACCOUNT = "root";
private const string _API_SYS_USER_LOGIN_BY_PWD = "/api/sys/user/login.by.pwd";
private const string _AUTH_SCHEMA = "Bearer";
private const string _PASSWORD = "1234qwer";
private const string _ACCOUNT = "root";
private const string _PASSWORD = "1234qwer";
private string _accessToken;
/// <summary>
@ -28,7 +26,7 @@ public abstract class WebApiTestBase<T>(WebApplicationFactory<T> factory, ITestO
{
var client = factory.CreateClient();
if (_accessToken == null) {
var loginRsp = await client.PostAsync(_API_SYS_USER_LOGIN_BY_PWD
var loginRsp = await client.PostAsync(Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD
, JsonContent.Create(
new LoginByPwdReq { Password = _PASSWORD, Account = _ACCOUNT }))
.ConfigureAwait(false);
@ -37,7 +35,8 @@ public abstract class WebApiTestBase<T>(WebApplicationFactory<T> factory, ITestO
_accessToken = loginRspObj.Data.AccessToken;
}
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(_AUTH_SCHEMA, _accessToken);
client.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA, _accessToken);
var ret = await client.PostAsync(url, content).ConfigureAwait(false);
testOutputHelper.WriteLine(await ret.Content.ReadAsStringAsync().ConfigureAwait(false));
return ret;