首页 > 编程语言 >ASP.NET Zero Authenticate 性能问题

ASP.NET Zero Authenticate 性能问题

时间:2024-04-02 19:25:06浏览次数:36  
标签:ASP AddTokenValidityKeyAsync userTokenRepository Zero Authenticate user var unit

前言

​伴随着ASP.NET Zero系统日渐运行,通过/api/TokenAuth/Authenticate获取Token的速度会逐渐变慢,到最后会呈现出一次获取会超过20秒或者导致超时的现象。

先说结论

导致问题产生的代码:

​ TokenAuthController > CreateJwtClaims > AddTokenValidityKeyAsync

await _userManager.AddTokenValidityKeyAsync(
    user,
    tokenValidityKey,
    expirationDate
);

问题

为什么一个获取Token的接口会出现如此性能问题呢?究竟是什么情况下会产生这样的问题?带着这样的疑问,开始了以下的debug之路。

一、调试Authenticate

  1. ​ 通过postman调用/api/TokenAuth/Authenticate

  2. 从调用结果看到,我们获取Token的总耗时是2.20s。

  3. 通过debug接口可以发现接口/api/TokenAuth/Authenticate的主要耗时点在CreateAccessToken,总共花费了2.02s

  4. 这段代码是在做什么呢?通过代码分析,我们可以知道他总共做了2件事情。第一件事情就是CreateJwtClaims,第二件事情就是通过JwtClaims创建JwtSecurityToken

  5. 再次对CreateAccessToken&CreateJwtClaims进行调试,从以下截图我们可以知道。罪魁祸首就是AddTokenValidityKeyAsync,基本的耗时都在这里。

  6. 通过ILSpy工具观看源码

    //AbpUserManager.cs	
    public virtual async Task RemoveTokenValidityKeyAsync(TUser user, string tokenValidityKey, CancellationToken cancellationToken = default(CancellationToken))
    {
        await AbpUserStore.RemoveTokenValidityKeyAsync(user, tokenValidityKey, cancellationToken);
    }
    
    //AbpUserStore.cs
    public virtual async Task AddTokenValidityKeyAsync(TUser user, string tokenValidityKey, DateTime expireDate, CancellationToken cancellationToken = default(CancellationToken))
    {
        cancellationToken.ThrowIfCancellationRequested();
        Check.NotNull<TUser>(user, "user");
        await RepositoryExtensions.EnsureCollectionLoadedAsync<TUser, long, UserToken>(UserRepository, user, (Expression<Func<TUser, IEnumerable<UserToken>>>)((TUser u) => u.Tokens), cancellationToken);
        user.Tokens.Add(new UserToken((AbpUserBase)(object)user, "TokenValidityKeyProvider", tokenValidityKey, null, expireDate));
    }
    
  7. 其中await RepositoryExtensions.EnsureCollectionLoadedAsync会加载所有导航实体,此操作会随着AbpUserTokens表的增大而耗费的时间加长。

二、解决问题

  1. 方法一:通过sql server 直接删除 AbpUserTokens 表数据

    --这区间内的数据是一年后过期的RefreshToken数据,AccessToken的失效日期是一天,基本不需要处理。
    DECLARE @dtnow datetime = getdate()
    DECLARE @BeginTime datetime = DATEADD(day, 2, @dtnow)
    DECLARE @EndTime datetime = DATEADD(year, 1, @dtnow)
    DELETE FROM AbpUserTokens WHERE Id IN (SELECT Id FROM AbpUserTokens WHERE ExpireDate > @BeginTime AND ExpireDate <= @EndTime)
    
  2. 方法二:通过ASP.NET Zero 框架中的 background job定时删除

    public class UserTokenExpirationWorker : PeriodicBackgroundWorkerBase, ISingletonDependency
        {
            private readonly IUnitOfWorkManager _unitOfWorkManager;
            private readonly IRepository<UserToken, long> _userTokenRepository;
            private readonly IRepository<Tenant> _tenantRepository;
            public UserTokenExpirationWorker(
                IUnitOfWorkManager unitOfWorkManager,
                IRepository<UserToken, long> userTokenRepository,
                IRepository<Tenant> tenantRepository,
                AbpTimer timer)
                : base(timer)
            {
                _userTokenRepository = userTokenRepository;
                _unitOfWorkManager = unitOfWorkManager;
                _tenantRepository = tenantRepository;
                Timer.Period = 1 * 1000 * 60; // 执行间隔时间;
            }
    
            [UnitOfWork]
            protected override void DoWork()
            {
                List<int> tenantIds;
                var utcNow = Clock.Now.ToUniversalTime();
                //由于AccessToken有效期是1天有效期,故此处是当前时间+2天。区间和范围都可以根据自己实际情况调整
                var beginTime = utcNow.AddDays(2);
                var endTime = utcNow.AddYears(1);
    
                using (var uow = _unitOfWorkManager.Begin())
                {
                    using (_unitOfWorkManager.Current.SetTenantId(null))
                    {
                        _userTokenRepository.Delete(t => t.ExpireDate > beginTime && t.ExpireDate <= endTime);
                        tenantIds = _tenantRepository.GetAll().Select(t => t.Id).ToList();
                        uow.Complete();
                    }
                }
    
                foreach (var tenantId in tenantIds)
                {
                    using (var uow = _unitOfWorkManager.Begin())
                    {
                        using (_unitOfWorkManager.Current.SetTenantId(tenantId))
                        {
                            _userTokenRepository.Delete(t => t.ExpireDate <= beginTime && t.ExpireDate <= endTime);
                            uow.Complete();
                        }
                    }
                }
            }
        }
    
  3. 执行完以上2种方法后再次调用接口/api/TokenAuth/Authenticate,仅耗时54ms,获取速度回归到正常水平。

三、参考

  1. _userManager.AddTokenValidityKeyAsync performance issue · Issue #5244 · aspnetboilerplate/aspnetboilerplate (github.com)

标签:ASP,AddTokenValidityKeyAsync,userTokenRepository,Zero,Authenticate,user,var,unit
From: https://www.cnblogs.com/YuYangBlogs/p/18110566

相关文章

  • .NET Aspire 中的服务发现
    .NETAspire中的服务发现https://www.nuget.org/packages/Microsoft.Extensions.ServiceDiscovery.NETAspire中的服务发现.NETAspire包含了对于开发阶段和测试阶段配置服务发现的功能。该功能通过提供基于配置的服务端点解析器来实现,来自.NETAspireAppHost项目中的服......
  • docker-compose 部署OWASP Juice Shop + CTFd
    项目介绍1.OWASPJuiceShop原文OWASPJuiceShopisprobablythemostmodernandsophisticatedinsecurewebapplication!Itcanbeusedinsecuritytrainings,awarenessdemos,CTFsandasaguineapigforsecuritytools!JuiceShopencompassesvulnerabili......
  • ASP.NET Core 给 PDF 加水印
    需要使用itext-dotnet:https://github.com/itext/itext-dotnet使用Nuget:Install-PackageitextInstall-Packageitext.bouncy-castle-adapter给PDF加水印publicstaticboolWatermarkPdf(stringinputPath,stringoutputPath,stringwatermarkImage,stringwaterma......
  • php采用aes算法,字符编码utf8,填充模式ECB,填充:ZeroPadding,编码:base64,实现加密
    原文:https://blog.csdn.net/yinxinyue0621/article/details/129813334一、php:三种填充模式的区别(PKCS7Padding/PKCS5Padding/ZeroPadding)(一)常见的三种填充方式:我们知道某些加密算法要求明文需要按一定长度对齐,叫做块大小(BlockSize),比如16字节,那么对于一段任意的数据,加密前需要......
  • Microsoft.AspNetCore.SignalR.Client中传送用户Token
    Microsoft.AspNetCore.SignalR.Client的.Net客户端传送Token的最佳方法!如下将Authorization标头添加到HubConnectionBuilder中,如下所示:对于不记名令牌->HubConnection=newHubConnectionBuilder().WithUrl($"https://10.0.2.2:5001/chatHub",(opts)=>......
  • 五、Yocto集成QT5(基于Raspberrypi 4B)
    Yocto集成QT5本篇文章为基于raspberrypi4B单板的yocto实战系列的第五篇文章:一、yocto编译raspberrypi4B并启动二、yocto集成ros2(基于raspberrypi4B)三、Yocto创建自定义的layer和image四、Yocto创建静态IP和VLAN本章节实操代码请查看github仓库:meta-rpi-robot......
  • 隐藏ASP.NET MVC的版本信息,使其不在HTTP Header中显示
    隐藏ASP.NETMVC的版本信息,使其不在HTTPHeader中显示。一、隐藏:X-AspNetMvc-Version在Global.asax文件的Application_Start方法中添加:1MvcHandler.DisableMvcResponseHeader= true;二、移除Header中的Server在Global.asax文件中添加:12345......
  • OWASP10
    访问控制崩溃未对通过身份验证的用户实施恰当的访问控制。攻击者可以利用这些缺陷访问未经授权的功能和数据,例如:访问其他用户的账户、查看敏感文件、修改其他用户的数据、更改访问权限等。eg:零元购通过修改URL、内部应用程序状态或HTML页面绕过访问控制检查,或简单地使用自定义......
  • ASP.NET Core 用密码加密 PDF
    需要使用itext-dotnet:https://github.com/itext/itext-dotnet使用Nuget:Install-PackageitextInstall-Packageitext.bouncy-castle-adapter使用密码加密PDF:publicstaticboolEncryptPdf(stringinputPath,stringoutputPath){boolresult=......
  • CF1634D Finding Zero
    原题链接每日一题系列。在做过CF1762DGCDQueries后发现这个题跟那个trick一模一样,所以切了,尽管做那道的时候没有想出来。每次任意选出四个数\(a,b,c,d\),规定\(a\leqb\leqc\leqd\)。考虑询问这四个数中所有组合。然后仅保留可能为\(0\)的位置,剩下的删掉。\(f(a,b......