From d575e76c9a1aacce3c277ab5dd9aacc8bdf65a1f Mon Sep 17 00:00:00 2001 From: LiuXin Date: Fri, 31 Oct 2025 14:15:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=A7=86=E9=A2=91token?= =?UTF-8?q?=E7=9A=84=E6=80=A7=E8=83=BD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DaHTokenService/TokenProviderService.cs | 91 +++++++++---------- .../Dahvision/DahuaGeneralCtlService.cs | 38 +------- 2 files changed, 45 insertions(+), 84 deletions(-) diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs index a05af23..bbfef32 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs @@ -1,8 +1,6 @@ using Common.Shared.Application.DaHua; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using MongoDB.Bson; -using OfficeOpenXml.FormulaParsing.LexicalAnalysis; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using System.Net.Http.Json; @@ -35,36 +33,29 @@ namespace Common.Shared.DomainService public async Task GetTokenAsync(string clientId) { - if (TokenCache.TokenMap.TryGetValue(clientId, out var tokenEntry) - && tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(1)) + if (TokenCache.TokenMap.TryGetValue(clientId, out var tokenEntry)) { - if (!IsTokenValid(tokenEntry.AccessToken!)) - { - _logger.LogWarning("从字典里匹配"); + // ✅ 快速通道:缓存有效直接返回 + if (tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(1) && IsTokenValid(tokenEntry.AccessToken)) + return tokenEntry.AccessToken; - return tokenEntry.AccessToken!; - } + // ✅ 异步刷新任务(不阻塞主线程) + _ = Task.Run(() => SafeRefreshTokenAsync(clientId, tokenEntry)); + return tokenEntry.AccessToken; // 临时返回旧 token } + // 缓存中没有,才走锁逻辑 var tokenLock = TokenLockProvider.GetLock(clientId); - _logger.LogWarning("枷锁"); await tokenLock.WaitAsync(); try { - // 加锁后再次检查,防止重复刷新 - if (TokenCache.TokenMap.TryGetValue(clientId, out tokenEntry) - && tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(5)) - { - if (!IsTokenValid(tokenEntry.AccessToken!)) - { - _logger.LogWarning("再次匹配"); - - return tokenEntry.AccessToken!; - } - } + if (TokenCache.TokenMap.TryGetValue(clientId, out tokenEntry) && + tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(1) && + IsTokenValid(tokenEntry.AccessToken)) + return tokenEntry.AccessToken; var refreshed = await TryRefreshOrLoginAsync(clientId, tokenEntry); - + TokenCache.TokenMap[clientId] = refreshed; return refreshed.AccessToken; } finally @@ -73,6 +64,21 @@ namespace Common.Shared.DomainService } } + // 异步后台刷新 + private async Task SafeRefreshTokenAsync(string clientId, TokenEntry? current) + { + try + { + var refreshed = await TryRefreshOrLoginAsync(clientId, current); + if (!string.IsNullOrWhiteSpace(refreshed.AccessToken)) + TokenCache.TokenMap[clientId] = refreshed; + } + catch (Exception ex) + { + _logger.LogError(ex, "后台刷新 Token 异常"); + } + } + private async Task TryRefreshOrLoginAsync(string clientId, TokenEntry? current) { try @@ -98,13 +104,11 @@ namespace Common.Shared.DomainService { AccessToken = string.Concat(result.Data.TokenType, " ", result.Data.AccessToken), - ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn) + ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn - 180) }; - _logger.LogInformation("Refresh 成功: {ClientId}", clientId); } else { - _logger.LogWarning("RefreshToken 失败,尝试重新登录"); refreshed = await GetDaHToken(); } } @@ -150,8 +154,7 @@ namespace Common.Shared.DomainService dto.ClientSecret = _configuration["DahuaAuth:ClientSecret"]!; dto.Password = _configuration["DahuaAuth:Password"]!; dto.Username = _configuration["DahuaAuth:Username"]!; - _logger.LogWarning("在GetDaHToken方法中的记录:Dahua Host = {Host}, ClientId = {ClientId}", - _configuration["DahuaAuth:Host"], _configuration["DahuaAuth:ClientId"], _configuration["DahuaAuth:ClientSecret"]); + DaHApiResult loginResult = await GetToken(dto); TokenEntry refreshed = new() @@ -179,7 +182,7 @@ namespace Common.Shared.DomainService result.Success = false; result.Code = "1005"; result.Msg = "刷新令牌不能为空"; - _logger.LogWarning("刷新大华令牌失败,刷新令牌不能为空"); + return result; } @@ -197,7 +200,6 @@ namespace Common.Shared.DomainService result.Success = false; result.Code = "1006"; result.Msg = "刷新大华令牌失败"; - _logger.LogWarning("刷新大华令牌失败,返回结果:{Result}", result); } } catch (Exception ex) @@ -221,11 +223,11 @@ namespace Common.Shared.DomainService try { var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/public-key"; - _logger.LogWarning($"获取的url{url}"); + using var resp = await _http.GetAsync(url); resp.EnsureSuccessStatusCode(); var json = await resp.Content.ReadAsStringAsync(); - _logger.LogWarning($"获取密钥的结果json{json}"); + var envelope = JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true @@ -233,7 +235,7 @@ namespace Common.Shared.DomainService if (envelope?.Data?.PublicKey is null or { Length: 0 }) { - _logger.LogWarning("获取大华公钥失败,返回结果:{Result}", json); + _logger.LogError("获取大华公钥失败,返回结果:{Result}", json); result.Success = false; result.Code = "1001"; result.Msg = "获取大华公钥失败"; @@ -267,7 +269,7 @@ namespace Common.Shared.DomainService result.Code = "1002"; result.Msg = "请求参数不能为空"; - _logger.LogWarning("获取大华登录令牌失败,参数不能为空"); + return result; } if (string.IsNullOrWhiteSpace(dto.Password)) @@ -275,27 +277,25 @@ namespace Common.Shared.DomainService result.Success = false; result.Code = "1003"; result.Msg = "密码不能为空"; - _logger.LogWarning("获取大华登录令牌失败,密码不能为空"); + return result; } try { var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/extend/token"; //必须加密 - _logger.LogWarning($"获取token的url{url}"); - _logger.LogWarning($"获取token的dto的json{dto.ToJson()}"); + dto.Password = EncryptByPublicKey(dto.Password, dto.PublicKey!); using var resp = await _http.PostAsJsonAsync(url, dto); resp.EnsureSuccessStatusCode(); - _logger.LogWarning("这是正式请求的:Dahua Host = {Host}, ClientId = {ClientId},ClientSecret={ClientSecret}", _configuration["DahuaAuth:Host"], _configuration["DahuaAuth:ClientId"], _configuration["DahuaAuth:ClientSecret"]); + var tokenInfo = await resp.Content.ReadFromJsonAsync>(); - _logger.LogWarning($"获取大华登录令牌返回结果:{tokenInfo.ToJson()}"); + if (tokenInfo == null || !result.Success || result.Code != "0") { result.Success = false; result.Code = "1004"; result.Msg = "获取大华登录令牌失败"; - _logger.LogWarning($"获取大华登录令牌失败,返回结果:{result}"); } result = tokenInfo!; //固定的拼接方式 @@ -306,7 +306,7 @@ namespace Common.Shared.DomainService { AccessToken = string.Concat(result!.Data.TokenType, " ", result.Data.AccessToken), - ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn) + ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn - 180) }; } else @@ -314,7 +314,6 @@ namespace Common.Shared.DomainService result.Success = false; result.Code = "1006"; result.Msg = "没拿到token"; - _logger.LogWarning($"获取大华登录令牌失败,返回结果:{result}"); } } catch (Exception ex) @@ -332,14 +331,10 @@ namespace Common.Shared.DomainService /// /// /// - public bool IsTokenValid(string token) + public bool IsTokenValid(string? token) { - // 避免 NullReferenceException - if (string.IsNullOrWhiteSpace(token) && token.Length < 10) - return false; - - // 统一写法,后续改条件只改这里 - return true; + if (string.IsNullOrWhiteSpace(token)) return false; + return token.Length > 10; } #region RES加密 diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs index 7d6c140..02b9726 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs @@ -45,7 +45,7 @@ namespace Video.DomainService // 1) 参数校验 + 早退 if (dto == null || string.IsNullOrWhiteSpace(dto.Data.ChannelId)) { - _logger.LogWarning("录像请求失败:通道ID不能为空"); + _logger.LogError("录像请求失败:通道ID不能为空"); return new DaHApiResult { Success = false, Code = "1007", Msg = "通道ID不能为空" }; } @@ -114,7 +114,6 @@ namespace Video.DomainService // 0) 参数校验 + 早退(按需增加更多必填校验) if (dto == null) { - _logger.LogWarning("查询录像信息失败:请求参数不能为空"); return new DaHApiResult { Success = false, Code = "1009", Msg = "请求参数不能为空" }; } @@ -122,7 +121,6 @@ namespace Video.DomainService var token = await _tokenProviderService.GetTokenAsync(clientId!); if (!_tokenProviderService.IsTokenValid(token)) { - _logger.LogWarning("查询普通录像信息列表:token无效"); return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Record/QueryRecords"; @@ -144,14 +142,12 @@ namespace Video.DomainService if (!resp.IsSuccessStatusCode) { - _logger.LogWarning("查询录像信息 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); return new DaHApiResult { Success = false, Code = "1008", Msg = $"HTTP错误 {(int)resp.StatusCode}" }; } var result = JsonSerializer.Deserialize>(body); if (result == null || !result.Success) { - _logger.LogWarning("查询录像信息业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1008", Msg = "查询录像信息失败" }; } @@ -175,7 +171,6 @@ namespace Video.DomainService { if (dto == null) { - _logger.LogWarning("通道分页查询失败:请求参数不能为空"); return new DaHApiResult { Success = false, Code = "1009", Msg = "请求参数不能为空" }; } @@ -183,7 +178,6 @@ namespace Video.DomainService var token = await _tokenProviderService.GetTokenAsync(clientId!); if (!_tokenProviderService.IsTokenValid(token)) { - _logger.LogWarning("通道分页查询失败:token无效"); return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; } @@ -206,14 +200,12 @@ namespace Video.DomainService if (!resp.IsSuccessStatusCode) { - _logger.LogWarning("通道分页查询 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); return new DaHApiResult { Success = false, Code = "1007", Msg = $"HTTP错误 {(int)resp.StatusCode}" }; } var result = JsonSerializer.Deserialize>(body); if (result == null || !result.Success) { - _logger.LogWarning("通道分页查询业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1007", Msg = $"通道分页查询失败" }; } @@ -378,7 +370,6 @@ namespace Video.DomainService // 参数校验 + 早退 if (dto == null || string.IsNullOrWhiteSpace(dto.Data.ChannelId)) { - _logger.LogWarning("录像请求失败:通道ID不能为空"); return new DaHApiResult { Success = false, Code = "1007", Msg = "通道ID不能为空" }; } @@ -387,7 +378,6 @@ namespace Video.DomainService var token = await _tokenProviderService.GetTokenAsync(clientId!); if (!_tokenProviderService.IsTokenValid(token)) { - _logger.LogWarning("rtsp录像回放:token无效"); return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; } @@ -424,7 +414,6 @@ namespace Video.DomainService var resultRecord = JsonSerializer.Deserialize>(bodys); if (resultRecord == null || !resultRecord.Success) { - _logger.LogWarning("查询普通录像信息: {Body}", bodys); return new DaHApiResult { Success = false, Code = "1008", Msg = "查询普通录像信息" }; } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Playback/StartPlaybackByTime"; @@ -450,14 +439,12 @@ namespace Video.DomainService if (!resp.IsSuccessStatusCode) { - _logger.LogWarning("录像请求 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); return new DaHApiResult { Success = false, Code = "1008", Msg = $"HTTP错误 {(int)resp.StatusCode}" }; } var result = JsonSerializer.Deserialize>(body); if (result == null || !result.Success) { - _logger.LogWarning("录像请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1008", Msg = "录像请求失败" }; } result.Data!.Url = UrlHostReplacer.ReplaceHost( @@ -487,7 +474,6 @@ namespace Video.DomainService { if (dto == null || dto.Data == null) { - _logger.LogWarning("实时流请求失败:请求参数不能为空"); return new DaHApiResult { Success = false, Code = "1005", Msg = "请求参数不能为空" }; } @@ -496,7 +482,6 @@ namespace Video.DomainService if (!_tokenProviderService.IsTokenValid(token)) { - _logger.LogWarning("rtsp实时预览接口方式:token无效"); return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; } @@ -522,15 +507,12 @@ namespace Video.DomainService var body = await reader.ReadToEndAsync(); if (!resp.IsSuccessStatusCode) { - _logger.LogWarning("实时流请求 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); - return new DaHApiResult { Success = false, Code = "1014", Msg = $"HTTP错误 {(int)resp.StatusCode}" }; } var result = JsonSerializer.Deserialize>(body); if (result == null || !result.Success) { - _logger.LogWarning("实时流请求业务反序列化失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1013", Msg = "实时流请求业务反序列化失败" }; } result.Data!.Url = UrlHostReplacer.ReplaceHost( @@ -544,7 +526,7 @@ namespace Video.DomainService catch (Exception ex) { _logger.LogError(ex, $"大华实时流请求出错{ex.Message}"); - _logger.LogWarning($"{ex.Message}"); + return new DaHApiResult { Success = false, Code = "1012", Msg = $"实时流请求失败{ex.Message}" }; } } @@ -561,7 +543,6 @@ namespace Video.DomainService var token = await _tokenProviderService.GetTokenAsync(clientId!); if (!_tokenProviderService.IsTokenValid(token)) { - _logger.LogWarning("下载:token无效"); return "下载地址无效"; } @@ -587,7 +568,6 @@ namespace Video.DomainService if (!_tokenProviderService.IsTokenValid(token)) { - _logger.LogWarning("抓拍接口方式:token无效"); return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; } @@ -610,20 +590,16 @@ namespace Video.DomainService var body = await reader.ReadToEndAsync(); if (!resp.IsSuccessStatusCode) { - _logger.LogWarning("抓拍 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); - return new DaHApiResult { Success = false, Code = "1014", Msg = $"抓拍错误 {(int)resp.StatusCode}" }; } var outer = JsonSerializer.Deserialize>(body); if (outer == null || !outer.Success || outer.Data == null) { - _logger.LogWarning("抓拍请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1013", Msg = "抓拍请求失败" }; } var result = JsonSerializer.Deserialize(outer.Data); if (result == null) { - _logger.LogWarning("抓拍请求业务反序列化失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1013", Msg = "抓拍反序列化失败" }; } @@ -654,7 +630,6 @@ namespace Video.DomainService if (!_tokenProviderService.IsTokenValid(token)) { - _logger.LogWarning("控制云台镜头接口方式:token无效"); return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; } @@ -676,20 +651,16 @@ namespace Video.DomainService var body = await reader.ReadToEndAsync(); if (!resp.IsSuccessStatusCode) { - _logger.LogWarning("控制云台镜头失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); - return new DaHApiResult { Success = false, Code = "1014", Msg = $"控制云台镜头错误 {(int)resp.StatusCode}" }; } var outer = JsonSerializer.Deserialize>(body); if (outer == null || !outer.Success || outer.Data == null) { - _logger.LogWarning("控制云台镜头请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1013", Msg = "控制云台镜头请求失败" }; } if (outer.Data == null) { - _logger.LogWarning("控制云台镜头请求业务反序列化失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1013", Msg = "控制云台镜头反序列化失败" }; } apiResult.Data = outer.Data; @@ -717,7 +688,6 @@ namespace Video.DomainService if (!_tokenProviderService.IsTokenValid(token)) { - _logger.LogWarning("云台方向控制接口方式:token无效"); return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; } @@ -739,20 +709,16 @@ namespace Video.DomainService var body = await reader.ReadToEndAsync(); if (!resp.IsSuccessStatusCode) { - _logger.LogWarning("云台方向控制失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); - return new DaHApiResult { Success = false, Code = "1014", Msg = $"云台方向控制错误 {(int)resp.StatusCode}" }; } var outer = JsonSerializer.Deserialize>(body); if (outer == null || !outer.Success || outer.Data == null) { - _logger.LogWarning("云台方向控制请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1013", Msg = "云台方向控制请求失败" }; } if (outer.Data == null) { - _logger.LogWarning("云台方向控制请求业务反序列化失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1013", Msg = "云台方向控制反序列化失败" }; } apiResult.Data = outer.Data;