From 42b78686512c8cf418d50eaf468b52c1da976780 Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 01:02:39 +0800 Subject: [PATCH] perf(auth): add DoLoginAsync, eliminate ThreadPool starvation on every login MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #5 - Add DoLoginAsync with full async/await implementation (6 former .Result/.Wait() calls) - DC.SaveChanges() → SaveChangesAsync() for MD5 auto-rehash path - Mark DoLogin [Obsolete], keep as GetAwaiter().GetResult() wrapper for backward compat - Update all 5 demo AccountControllers + LoginController to await DoLoginAsync Co-Authored-By: Claude Sonnet 4.6 --- .../_Admin/Controllers/AccountController.cs | 4 +- .../ApiControllers/AccountController.cs | 4 +- .../Controllers/LoginController.cs | 2 +- .../_Admin/Controllers/AccountController.cs | 4 +- .../_Admin/Controllers/AccountController.cs | 4 +- .../_Admin/Controllers/AccountController.cs | 4 +- src/WalkingTec.Mvvm.Core/WTMContext.cs | 48 +++++++------------ 7 files changed, 28 insertions(+), 42 deletions(-) diff --git a/demo/WalkingTec.Mvvm.BlazorDemo/WalkingTec.Mvvm.BlazorDemo/_Admin/Controllers/AccountController.cs b/demo/WalkingTec.Mvvm.BlazorDemo/WalkingTec.Mvvm.BlazorDemo/_Admin/Controllers/AccountController.cs index 930a8cf3e..09ff16eef 100644 --- a/demo/WalkingTec.Mvvm.BlazorDemo/WalkingTec.Mvvm.BlazorDemo/_Admin/Controllers/AccountController.cs +++ b/demo/WalkingTec.Mvvm.BlazorDemo/WalkingTec.Mvvm.BlazorDemo/_Admin/Controllers/AccountController.cs @@ -32,7 +32,7 @@ public class AccountController : BaseApiController public async Task Login([FromForm] string account, [FromForm] string password, [FromForm] string tenant = null, [FromForm] bool rememberLogin = false) { - var user = Wtm.DoLogin(account, password, tenant); + var user = await Wtm.DoLoginAsync(account, password, tenant); if (user == null) { return BadRequest(Localizer["Sys.LoginFailed"].Value); @@ -62,7 +62,7 @@ public async Task Login([FromForm] string account, [FromForm] str [HttpPost("LoginJwt")] public async Task LoginJwt(SimpleLogin loginInfo) { - var user = Wtm.DoLogin(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); + var user = await Wtm.DoLoginAsync(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); if (user == null) { ModelState.AddModelError(" ", Localizer["Sys.LoginFailed"]); diff --git a/demo/WalkingTec.Mvvm.Demo/Areas/_Admin/ApiControllers/AccountController.cs b/demo/WalkingTec.Mvvm.Demo/Areas/_Admin/ApiControllers/AccountController.cs index 5e6ac5d36..c693ede36 100644 --- a/demo/WalkingTec.Mvvm.Demo/Areas/_Admin/ApiControllers/AccountController.cs +++ b/demo/WalkingTec.Mvvm.Demo/Areas/_Admin/ApiControllers/AccountController.cs @@ -32,7 +32,7 @@ public class AccountController : BaseApiController public async Task Login([FromForm] string account, [FromForm] string password, [FromForm] string tenant = null, [FromForm] bool rememberLogin = false) { - var user = Wtm.DoLogin(account, password, tenant); + var user = await Wtm.DoLoginAsync(account, password, tenant); if (user == null) { return BadRequest(Localizer["Sys.LoginFailed"].Value); @@ -62,7 +62,7 @@ public async Task Login([FromForm] string account, [FromForm] str [HttpPost("LoginJwt")] public async Task LoginJwt(SimpleLogin loginInfo) { - var user = Wtm.DoLogin(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); + var user = await Wtm.DoLoginAsync(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); if (user == null) { ModelState.AddModelError(" ", Localizer["Sys.LoginFailed"]); diff --git a/demo/WalkingTec.Mvvm.Demo/Controllers/LoginController.cs b/demo/WalkingTec.Mvvm.Demo/Controllers/LoginController.cs index 812bb6e1c..47a7eaaba 100644 --- a/demo/WalkingTec.Mvvm.Demo/Controllers/LoginController.cs +++ b/demo/WalkingTec.Mvvm.Demo/Controllers/LoginController.cs @@ -47,7 +47,7 @@ public async Task Login(LoginVM vm) return View(vm); } } - var user = Wtm.DoLogin(vm.ITCode, vm.Password, vm.Tenant); + var user = await Wtm.DoLoginAsync(vm.ITCode, vm.Password, vm.Tenant); if (user == null) { vm.MSD.AddModelError("", Localizer["Sys.LoginFailed"]); diff --git a/demo/WalkingTec.Mvvm.ReactDemo/Areas/_Admin/Controllers/AccountController.cs b/demo/WalkingTec.Mvvm.ReactDemo/Areas/_Admin/Controllers/AccountController.cs index 0f0e87c07..81dce9a3e 100644 --- a/demo/WalkingTec.Mvvm.ReactDemo/Areas/_Admin/Controllers/AccountController.cs +++ b/demo/WalkingTec.Mvvm.ReactDemo/Areas/_Admin/Controllers/AccountController.cs @@ -32,7 +32,7 @@ public class AccountController : BaseApiController public async Task Login([FromForm] string account, [FromForm] string password, [FromForm] string tenant = null, [FromForm] bool rememberLogin = false) { - var user = Wtm.DoLogin(account, password, tenant); + var user = await Wtm.DoLoginAsync(account, password, tenant); if (user == null) { return BadRequest(Localizer["Sys.LoginFailed"].Value); @@ -62,7 +62,7 @@ public async Task Login([FromForm] string account, [FromForm] str [HttpPost("LoginJwt")] public async Task LoginJwt(SimpleLogin loginInfo) { - var user = Wtm.DoLogin(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); + var user = await Wtm.DoLoginAsync(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); if (user == null) { ModelState.AddModelError(" ", Localizer["Sys.LoginFailed"]); diff --git a/demo/WalkingTec.Mvvm.Vue3Demo/Areas/_Admin/Controllers/AccountController.cs b/demo/WalkingTec.Mvvm.Vue3Demo/Areas/_Admin/Controllers/AccountController.cs index 975e70f89..42c9aeb95 100644 --- a/demo/WalkingTec.Mvvm.Vue3Demo/Areas/_Admin/Controllers/AccountController.cs +++ b/demo/WalkingTec.Mvvm.Vue3Demo/Areas/_Admin/Controllers/AccountController.cs @@ -32,7 +32,7 @@ public class AccountController : BaseApiController public async Task Login([FromForm] string account, [FromForm] string password, [FromForm] string tenant = null, [FromForm] bool rememberLogin = false) { - var user = Wtm.DoLogin(account, password, tenant); + var user = await Wtm.DoLoginAsync(account, password, tenant); if (user == null) { return BadRequest(Localizer["Sys.LoginFailed"].Value); @@ -62,7 +62,7 @@ public async Task Login([FromForm] string account, [FromForm] str [HttpPost("LoginJwt")] public async Task LoginJwt(SimpleLogin loginInfo) { - var user = Wtm.DoLogin(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); + var user = await Wtm.DoLoginAsync(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); if (user == null) { ModelState.AddModelError(" ", Localizer["Sys.LoginFailed"]); diff --git a/demo/WalkingTec.Mvvm.VueDemo/Areas/_Admin/Controllers/AccountController.cs b/demo/WalkingTec.Mvvm.VueDemo/Areas/_Admin/Controllers/AccountController.cs index 0f0e87c07..81dce9a3e 100644 --- a/demo/WalkingTec.Mvvm.VueDemo/Areas/_Admin/Controllers/AccountController.cs +++ b/demo/WalkingTec.Mvvm.VueDemo/Areas/_Admin/Controllers/AccountController.cs @@ -32,7 +32,7 @@ public class AccountController : BaseApiController public async Task Login([FromForm] string account, [FromForm] string password, [FromForm] string tenant = null, [FromForm] bool rememberLogin = false) { - var user = Wtm.DoLogin(account, password, tenant); + var user = await Wtm.DoLoginAsync(account, password, tenant); if (user == null) { return BadRequest(Localizer["Sys.LoginFailed"].Value); @@ -62,7 +62,7 @@ public async Task Login([FromForm] string account, [FromForm] str [HttpPost("LoginJwt")] public async Task LoginJwt(SimpleLogin loginInfo) { - var user = Wtm.DoLogin(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); + var user = await Wtm.DoLoginAsync(loginInfo.Account, loginInfo.Password, loginInfo.Tenant); if (user == null) { ModelState.AddModelError(" ", Localizer["Sys.LoginFailed"]); diff --git a/src/WalkingTec.Mvvm.Core/WTMContext.cs b/src/WalkingTec.Mvvm.Core/WTMContext.cs index 18e12e582..127f65cbc 100644 --- a/src/WalkingTec.Mvvm.Core/WTMContext.cs +++ b/src/WalkingTec.Mvvm.Core/WTMContext.cs @@ -287,7 +287,7 @@ public virtual LoginUserInfo { return null; } - var user = DoLogin(itcode, null, null); + var user = DoLoginAsync(itcode, null, null).GetAwaiter().GetResult(); return user; } @@ -365,11 +365,11 @@ public void SetServiceProvider(IServiceProvider sp) this._serviceProvider = sp; } - public LoginUserInfo DoLogin(string username, string password, string tenant) + public async Task DoLoginAsync(string username, string password, string tenant) { if(string.IsNullOrEmpty(tenant)) { - tenant = DC.TenantCode; + tenant = DC.TenantCode; } if (tenant == null && HttpContext.User.Identity.IsAuthenticated) { @@ -382,12 +382,12 @@ public LoginUserInfo DoLogin(string username, string password, string tenant) { remoteToken = HttpContext.User.Claims.Where(x => x.Type == AuthConstants.JwtClaimTypes.RToken).Select(x => x.Value).FirstOrDefault(); } - LoginUserInfo rv = null; + LoginUserInfo rv = null; if (string.IsNullOrEmpty(remoteToken) == false) { Dictionary headers = new Dictionary(); headers.Add("Authorization", "Bearer " + remoteToken); - var user = CallAPI("mainhost", "/api/_account/checkuserinfo?IsApi=false", HttpMethodEnum.GET, new { }, 10, headers: headers).Result; + var user = await CallAPI("mainhost", "/api/_account/checkuserinfo?IsApi=false", HttpMethodEnum.GET, new { }, 10, headers: headers); rv = user.Data; if (rv != null) { @@ -396,13 +396,13 @@ public LoginUserInfo DoLogin(string username, string password, string tenant) } else if(string.IsNullOrEmpty(password)==false) { - var loginjwt = CallAPI("mainhost", "/api/_account/loginjwt", HttpMethodEnum.POST, new { Account = username, Password = password }, 10).Result; + var loginjwt = await CallAPI("mainhost", "/api/_account/loginjwt", HttpMethodEnum.POST, new { Account = username, Password = password }, 10); if (string.IsNullOrEmpty(loginjwt?.Data?.AccessToken) == false) { remoteToken = loginjwt?.Data?.AccessToken; Dictionary headers = new Dictionary(); headers.Add("Authorization", "Bearer " + remoteToken); - var user = CallAPI("mainhost", "/api/_account/checkuserinfo?IsApi=false", HttpMethodEnum.GET, new { }, 10, headers: headers).Result; + var user = await CallAPI("mainhost", "/api/_account/checkuserinfo?IsApi=false", HttpMethodEnum.GET, new { }, 10, headers: headers); rv = user.Data; if (rv != null) { @@ -412,16 +412,7 @@ public LoginUserInfo DoLogin(string username, string password, string tenant) } if (rv != null) { - //var cacheKey = $"{GlobalConstants.CacheKey.UserInfo}:{rv.ITCode + "$`$" + rv.TenantCode}"; - //var cacheuser = Cache.Get(cacheKey); - //if (cacheuser != null && cacheuser.TimeTick >= rv.TimeTick) - //{ - // rv = cacheuser; - //} - //else - //{ - rv.LoadBasicInfoAsync(this).Wait(); - //} + await rv.LoadBasicInfoAsync(this); } return rv; } @@ -430,7 +421,6 @@ public LoginUserInfo DoLogin(string username, string password, string tenant) bool exist = false; username = HttpContext.User.Claims.Where(x => x.Type == AuthConstants.JwtClaimTypes.Subject).Select(x => x.Value).FirstOrDefault() ?? username; var ct = GlobaInfo.AllTenant.Where(x => x.TCode == tenant).FirstOrDefault(); - //如果找不到指定的tenant,说明租户不存在,直接返回null if(ct == null && string.IsNullOrEmpty(tenant) == false) { return null; @@ -462,7 +452,6 @@ public LoginUserInfo DoLogin(string username, string password, string tenant) else { exist = true; - // Auto-upgrade: if old MD5 hash, rehash on successful login if (verifyResult == PasswordVerifyResult.SuccessRehashNeeded) { var fullUser = BaseUserQuery.IgnoreQueryFilters() @@ -471,7 +460,7 @@ public LoginUserInfo DoLogin(string username, string password, string tenant) if (fullUser != null) { fullUser.Password = PasswordHashHelper.HashPassword(password); - DC.SaveChanges(); + await DC.SaveChangesAsync(); } } } @@ -487,24 +476,21 @@ public LoginUserInfo DoLogin(string username, string password, string tenant) ITCode = username, TenantCode = tenant }; - //var cacheKey = $"{GlobalConstants.CacheKey.UserInfo}:{username + "$`$" + tenant}"; - //var cacheuser = Cache.Get(cacheKey); - //if (cacheuser != null) - //{ - // user = cacheuser; - //} - //else - //{ - user.LoadBasicInfoAsync(this).Wait(); - //} + await user.LoadBasicInfoAsync(this); user.RemoteToken = null; var authService = HttpContext.RequestServices.GetService(typeof(ITokenService)) as ITokenService; - var token = authService.IssueTokenAsync(user).Result; + var token = await authService.IssueTokenAsync(user); user.RemoteToken = token.AccessToken; return user; } } + [Obsolete("Use DoLoginAsync to avoid ThreadPool starvation. DoLogin blocks threads on every auth request.")] + public LoginUserInfo DoLogin(string username, string password, string tenant) + { + return DoLoginAsync(username, password, tenant).GetAwaiter().GetResult(); + } + public Token RefreshToken() { if(LoginUserInfo == null)