|
|
|
|
@ -1,10 +1,8 @@ |
|
|
|
|
using Common.Shared.Application.DaHua; |
|
|
|
|
using Common.Shared.DomainService; |
|
|
|
|
using Microsoft.Extensions.Configuration; |
|
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
|
using Org.BouncyCastle.Crypto.Parameters; |
|
|
|
|
using Org.BouncyCastle.Security; |
|
|
|
|
using System.Net.Http.Json; |
|
|
|
|
using System.Security.Cryptography; |
|
|
|
|
using System.Text.Json; |
|
|
|
|
using Video.Application; |
|
|
|
|
|
|
|
|
|
@ -14,108 +12,25 @@ namespace Video.DomainService |
|
|
|
|
{ |
|
|
|
|
private readonly ILogger<DahuaGeneralCtlService> _logger; |
|
|
|
|
private readonly IConfiguration _configuration; |
|
|
|
|
private readonly HttpClient _http; |
|
|
|
|
private readonly ITokenProviderService _tokenProviderService; |
|
|
|
|
// private readonly HttpClient _http; |
|
|
|
|
|
|
|
|
|
public DahuaGeneralCtlService(ILogger<DahuaGeneralCtlService> logger, IConfiguration configuration, HttpClient http) |
|
|
|
|
public DahuaGeneralCtlService(ILogger<DahuaGeneralCtlService> logger, IConfiguration configuration, ITokenProviderService tokenProviderService) |
|
|
|
|
{ |
|
|
|
|
_logger = logger; |
|
|
|
|
_configuration = configuration; |
|
|
|
|
_http = http; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 获取公钥 |
|
|
|
|
/// </summary> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
/// <exception cref="NotImplementedException"></exception> |
|
|
|
|
public async Task<DaHApiResult<PublicKeyDto>> GetPublicKey() |
|
|
|
|
{ |
|
|
|
|
DaHApiResult<PublicKeyDto> result = new() { Success = true, Code = "0", Data = new PublicKeyDto() { PublicKey = "" } }; |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/public-key"; |
|
|
|
|
_tokenProviderService = tokenProviderService; |
|
|
|
|
|
|
|
|
|
using var resp = await _http.GetAsync(url); |
|
|
|
|
resp.EnsureSuccessStatusCode(); |
|
|
|
|
var json = await resp.Content.ReadAsStringAsync(); |
|
|
|
|
var envelope = JsonSerializer.Deserialize<DaHApiResult<PublicKeyDto>>(json, new JsonSerializerOptions |
|
|
|
|
{ |
|
|
|
|
PropertyNameCaseInsensitive = true |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
if (envelope?.Data?.PublicKey is null or { Length: 0 }) |
|
|
|
|
{ |
|
|
|
|
_logger.LogWarning("获取大华公钥失败,返回结果:{Result}", json); |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1001"; |
|
|
|
|
result.Msg = "获取大华公钥失败"; |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
result.Data.PublicKey = envelope.Data.PublicKey; |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
_logger.LogWarning(ex, "大华平台获取公钥出错"); |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
//_http = http; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 获取token |
|
|
|
|
/// 开发测试的时候,忽略证书 |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="dto"></param> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
public async Task<DaHApiResult<LoginResDto>> GetToken(LoginRequestDto dto) |
|
|
|
|
private static readonly HttpClient _http = new HttpClient(new HttpClientHandler |
|
|
|
|
{ |
|
|
|
|
DaHApiResult<LoginResDto> result = new() { Success = true, Code = "0" }; |
|
|
|
|
if (dto is null) |
|
|
|
|
{ |
|
|
|
|
result.Success = false; |
|
|
|
|
|
|
|
|
|
result.Code = "1002"; |
|
|
|
|
result.Msg = "请求参数不能为空"; |
|
|
|
|
_logger.LogWarning("获取大华登录令牌失败,参数不能为空"); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
if (string.IsNullOrWhiteSpace(dto.Password)) |
|
|
|
|
{ |
|
|
|
|
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"; |
|
|
|
|
//必须加密 |
|
|
|
|
dto.Password = EncryptByPublicKey(dto.Password, dto.PublicKey!); |
|
|
|
|
using var resp = await _http.PostAsJsonAsync(url, dto); |
|
|
|
|
resp.EnsureSuccessStatusCode(); |
|
|
|
|
|
|
|
|
|
var tokenInfo = await resp.Content.ReadFromJsonAsync<DaHApiResult<LoginResDto>>(); |
|
|
|
|
|
|
|
|
|
if (tokenInfo == null || !result.Success || result.Code != "0") |
|
|
|
|
{ |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1004"; |
|
|
|
|
result.Msg = "获取大华登录令牌失败"; |
|
|
|
|
_logger.LogWarning("获取大华登录令牌失败,返回结果:{Result}", result); |
|
|
|
|
} |
|
|
|
|
//固定的拼接方式 |
|
|
|
|
result.Data.AccessToken = string.Concat(tokenInfo!.Data.TokenType, " ", tokenInfo.Data.AccessToken); |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
_logger.LogError(ex, "获取大华登录令牌出错"); |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1004"; |
|
|
|
|
result.Msg = "获取大华登录令牌失败"; |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 录像回放 |
|
|
|
|
@ -136,9 +51,7 @@ namespace Video.DomainService |
|
|
|
|
{ |
|
|
|
|
// 2) Token:优先入参,其次缓存/获取(建议返回完整的 "Bearer xxx") |
|
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
|
|
|
var token = string.IsNullOrWhiteSpace(dto.Token) |
|
|
|
|
? await GetCachedOrFetchTokenAsync(clientId) |
|
|
|
|
: dto.Token; |
|
|
|
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
|
|
|
|
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; |
|
|
|
|
|
|
|
|
|
@ -190,9 +103,7 @@ namespace Video.DomainService |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
|
|
|
var token = string.IsNullOrWhiteSpace(dto.Token) |
|
|
|
|
? await GetCachedOrFetchTokenAsync(clientId) // 建议用这个轻量封装;返回完整 "Bearer xxx" |
|
|
|
|
: dto.Token; |
|
|
|
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
|
|
|
|
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Record/QueryRecords"; |
|
|
|
|
|
|
|
|
|
@ -229,54 +140,6 @@ namespace Video.DomainService |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 刷新token,2个小时过期的 |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="dto"></param> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
/// <exception cref="ArgumentNullException"></exception> |
|
|
|
|
/// <exception cref="ArgumentException"></exception> |
|
|
|
|
/// <exception cref="InvalidOperationException"></exception> |
|
|
|
|
public async Task<DaHApiResult<TokenResDto>> RefreshToken(RefreshTokenReqDto dto) |
|
|
|
|
{ |
|
|
|
|
DaHApiResult<TokenResDto> result = new DaHApiResult<TokenResDto>() { Success = true, Code = "0" }; |
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(dto.RefreshToken)) |
|
|
|
|
{ |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1005"; |
|
|
|
|
result.Msg = "刷新令牌不能为空"; |
|
|
|
|
_logger.LogWarning("刷新大华令牌失败,刷新令牌不能为空"); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/extend/refresh/token"; |
|
|
|
|
|
|
|
|
|
using var resp = await _http.PostAsJsonAsync(url, dto); |
|
|
|
|
resp.EnsureSuccessStatusCode(); |
|
|
|
|
|
|
|
|
|
result = await resp.Content.ReadFromJsonAsync<DaHApiResult<TokenResDto>>(); |
|
|
|
|
|
|
|
|
|
if (!result.Success || result.Code != "0") |
|
|
|
|
{ |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1006"; |
|
|
|
|
result.Msg = "刷新大华令牌失败"; |
|
|
|
|
_logger.LogWarning("刷新大华令牌失败,返回结果:{Result}", result); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
_logger.LogError(ex, "刷新大华令牌出错"); |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1006"; |
|
|
|
|
result.Msg = "刷新大华令牌失败"; |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 设备通道分页查询 |
|
|
|
|
/// </summary> |
|
|
|
|
@ -293,9 +156,7 @@ namespace Video.DomainService |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
|
|
|
var token = string.IsNullOrWhiteSpace(dto.Token) |
|
|
|
|
? await GetCachedOrFetchTokenAsync(clientId) // 建议统一用这个轻量封装 |
|
|
|
|
: dto.Token; // 约定这里是完整 "Bearer xxx" |
|
|
|
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
|
|
|
|
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-brm/1.2.0/device/channel/subsystem/page"; |
|
|
|
|
|
|
|
|
|
@ -351,9 +212,7 @@ namespace Video.DomainService |
|
|
|
|
{ |
|
|
|
|
// 2) Token:优先用入参;否则走缓存/获取(建议返回已带前缀的 "Bearer xxx") |
|
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
|
|
|
var token = string.IsNullOrWhiteSpace(dto.Data.Token) |
|
|
|
|
? await GetCachedOrFetchTokenAsync(clientId) // 见下方简版实现 |
|
|
|
|
: dto.Data.Token; |
|
|
|
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
|
|
|
|
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/realtime"; |
|
|
|
|
|
|
|
|
|
@ -440,43 +299,13 @@ namespace Video.DomainService |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 获取AccessToken |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="clientId"></param> |
|
|
|
|
/// <param name="ct"></param> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
/// <exception cref="NotImplementedException"></exception> |
|
|
|
|
public async Task<string> GetAccessTokenAsync(string? clientId, CancellationToken ct = default) |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
TokenEntry refreshed = new(); |
|
|
|
|
if (clientId == null || clientId == "") |
|
|
|
|
{ |
|
|
|
|
clientId = _configuration["DahuaAuth:ClientId"]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!TokenCache.TokenMap.TryGetValue(clientId!, out var entry) || TokenIsExpiring(entry)) |
|
|
|
|
{ |
|
|
|
|
refreshed = await TryRefreshOrLoginAsync(clientId!, entry); |
|
|
|
|
} |
|
|
|
|
return refreshed.AccessToken; |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
_logger.LogError(ex, "获取大华AccessToken出错"); |
|
|
|
|
return string.Empty; // 返回空字符串表示获取失败 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// rtsp录像回放 |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="dto"></param> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
/// <exception cref="NotImplementedException"></exception> |
|
|
|
|
public async Task<DaHApiResult<UrlDataDto>> RtspPlaybackByTime(PlaybackReqDto dto) |
|
|
|
|
public async Task<DaHApiResult<UrlDataDto>> RtspPlaybackByTime(RtspPlaybackReqDto dto) |
|
|
|
|
{ |
|
|
|
|
// 参数校验 + 早退 |
|
|
|
|
if (dto == null || string.IsNullOrWhiteSpace(dto.ChannelId)) |
|
|
|
|
@ -487,11 +316,9 @@ namespace Video.DomainService |
|
|
|
|
|
|
|
|
|
// 先用缓存里的 token,不足5分钟过期再刷新(按你之前的口径来) |
|
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
|
|
|
var token = string.IsNullOrWhiteSpace(dto.Token) |
|
|
|
|
? await GetCachedOrFetchTokenAsync(clientId) |
|
|
|
|
: dto.Token; |
|
|
|
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
|
|
|
|
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; |
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Playback/StartPlaybackByTime"; |
|
|
|
|
|
|
|
|
|
using var req = new HttpRequestMessage(HttpMethod.Post, url) |
|
|
|
|
{ |
|
|
|
|
@ -532,7 +359,7 @@ namespace Video.DomainService |
|
|
|
|
/// <param name="dto"></param> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
/// <exception cref="NotImplementedException"></exception> |
|
|
|
|
public async Task<DaHApiResult<UrlDataDto>> RtspStartVideoUrl(StreamReqDto dto) |
|
|
|
|
public async Task<DaHApiResult<UrlDataDto>> RtspStartVideoUrl(StreamRtspReqDto dto) |
|
|
|
|
{ |
|
|
|
|
if (dto == null || dto.Data == null) |
|
|
|
|
{ |
|
|
|
|
@ -541,9 +368,7 @@ namespace Video.DomainService |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
|
|
|
var token = string.IsNullOrWhiteSpace(dto.Data.Token) |
|
|
|
|
? await GetCachedOrFetchTokenAsync(clientId) |
|
|
|
|
: dto.Data.Token; |
|
|
|
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
|
|
|
|
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/MTS/Video/StartVideo"; |
|
|
|
|
|
|
|
|
|
@ -565,7 +390,7 @@ namespace Video.DomainService |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var result = JsonSerializer.Deserialize<DaHApiResult<UrlDataDto>>(body); |
|
|
|
|
if (result == null || !result.Success || result.Code != "0") |
|
|
|
|
if (result == null || !result.Success || result.Code != "100") |
|
|
|
|
{ |
|
|
|
|
_logger.LogWarning("实时流请求业务失败: {Body}", body); |
|
|
|
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1010", Msg = "实时流请求失败" }; |
|
|
|
|
@ -579,171 +404,5 @@ namespace Video.DomainService |
|
|
|
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1010", Msg = "实时流请求失败" }; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#region GetToken的辅助方法 |
|
|
|
|
|
|
|
|
|
private async Task<string> GetCachedOrFetchTokenAsync(string clientId) |
|
|
|
|
{ |
|
|
|
|
if (TokenCache.TokenMap.TryGetValue(clientId, out var e) && e != null && e.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(5)) |
|
|
|
|
return e.AccessToken; // 缓存可用 |
|
|
|
|
|
|
|
|
|
var refreshed = await GetAccessTokenAsync(clientId); |
|
|
|
|
return refreshed; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static bool TokenIsExpiring(TokenEntry entry) |
|
|
|
|
{ |
|
|
|
|
return entry.ExpireAt <= DateTimeOffset.UtcNow.AddMinutes(5); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private async Task<TokenEntry> TryRefreshOrLoginAsync(string clientId, TokenEntry? current) |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
TokenEntry refreshed; |
|
|
|
|
|
|
|
|
|
if (current != null) |
|
|
|
|
{ |
|
|
|
|
RefreshTokenReqDto refreshTokenReqDto = new() |
|
|
|
|
{ |
|
|
|
|
ClientId = clientId, |
|
|
|
|
RefreshToken = current.RefreshToken, |
|
|
|
|
ClientSecret = _configuration["DahuaAuth:ClientSecret"], |
|
|
|
|
GrantType = "refresh_token" |
|
|
|
|
}; |
|
|
|
|
//刷新token |
|
|
|
|
var result = await this.RefreshToken(refreshTokenReqDto); |
|
|
|
|
refreshed = new TokenEntry |
|
|
|
|
{ |
|
|
|
|
AccessToken = result.Data.AccessToken, |
|
|
|
|
RefreshToken = result.Data.RefreshToken, |
|
|
|
|
ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn) |
|
|
|
|
}; |
|
|
|
|
_logger.LogWarning("Refresh 成功: {ClientId}", clientId); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
var publicKeyResult = await this.GetPublicKey(); |
|
|
|
|
if (publicKeyResult != null && publicKeyResult.Data != null) |
|
|
|
|
{ |
|
|
|
|
_logger.LogWarning("获取公钥成功: {PublicKey}", publicKeyResult.Data.PublicKey); |
|
|
|
|
|
|
|
|
|
LoginRequestDto loginRequestDto = new() |
|
|
|
|
{ |
|
|
|
|
ClientId = clientId, |
|
|
|
|
ClientSecret = _configuration["DahuaAuth:ClientSecret"], |
|
|
|
|
Username = _configuration["DahuaAuth:Username"], |
|
|
|
|
Password = _configuration["DahuaAuth:Password"], |
|
|
|
|
PublicKey = publicKeyResult.Data.PublicKey, |
|
|
|
|
GrantType = "password", |
|
|
|
|
VerifyCodeFlag = 0 // 默认不开启动态验证码 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
var result = await this.GetToken(loginRequestDto); |
|
|
|
|
if (result.Data != null) |
|
|
|
|
{ |
|
|
|
|
refreshed = new TokenEntry |
|
|
|
|
{ |
|
|
|
|
AccessToken = string.Concat(result!.Data.TokenType, " ", result.Data.AccessToken), |
|
|
|
|
RefreshToken = result.Data.RefreshToken, |
|
|
|
|
ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn) |
|
|
|
|
}; |
|
|
|
|
_logger.LogWarning("Login 成功: {ClientId}", clientId); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
_logger.LogWarning("获取公钥失败"); |
|
|
|
|
return new TokenEntry |
|
|
|
|
{ |
|
|
|
|
AccessToken = string.Empty, |
|
|
|
|
RefreshToken = string.Empty, |
|
|
|
|
ExpireAt = DateTimeOffset.UtcNow.AddMinutes(5) // 设置一个短期的过期时间,避免后续调用失败 |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
_logger.LogWarning("获取公钥失败"); |
|
|
|
|
return new TokenEntry |
|
|
|
|
{ |
|
|
|
|
AccessToken = string.Empty, |
|
|
|
|
RefreshToken = string.Empty, |
|
|
|
|
ExpireAt = DateTimeOffset.UtcNow.AddMinutes(5) // 设置一个短期的过期时间,避免后续调用失败 |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TokenCache.TokenMap[clientId] = refreshed; |
|
|
|
|
return refreshed; |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
_logger.LogError(ex, "获取 token 失败:{ClientId}", clientId); |
|
|
|
|
return new TokenEntry |
|
|
|
|
{ |
|
|
|
|
AccessToken = string.Empty, |
|
|
|
|
RefreshToken = string.Empty, |
|
|
|
|
ExpireAt = DateTimeOffset.UtcNow.AddMinutes(5) // 设置一个短期的过期时间,避免后续调用失败 |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endregion GetToken的辅助方法 |
|
|
|
|
|
|
|
|
|
#region RES加密 |
|
|
|
|
|
|
|
|
|
private static String EncryptByPublicKey(String context, String publicKey) |
|
|
|
|
{ |
|
|
|
|
RSACryptoServiceProvider rsa = new(); |
|
|
|
|
|
|
|
|
|
rsa.ImportParameters(FromXmlStringExtensions(ConvertToXmlPublicJavaKey(publicKey))); |
|
|
|
|
byte[] byteText = System.Text.Encoding.UTF8.GetBytes(context); |
|
|
|
|
byte[] byteEntry = rsa.Encrypt(byteText, false); |
|
|
|
|
return Convert.ToBase64String(byteEntry); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static RSAParameters FromXmlStringExtensions(string xmlString) |
|
|
|
|
{ |
|
|
|
|
RSAParameters parameters = new RSAParameters(); |
|
|
|
|
|
|
|
|
|
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument(); |
|
|
|
|
xmlDoc.LoadXml(xmlString); |
|
|
|
|
|
|
|
|
|
if (xmlDoc.DocumentElement!.Name.Equals("RSAKeyValue")) |
|
|
|
|
{ |
|
|
|
|
foreach (System.Xml.XmlNode node in xmlDoc.DocumentElement.ChildNodes) |
|
|
|
|
{ |
|
|
|
|
switch (node.Name) |
|
|
|
|
{ |
|
|
|
|
case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
throw new Exception("Invalid XML RSA key."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return parameters; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static string ConvertToXmlPublicJavaKey(string publicJavaKey) |
|
|
|
|
{ |
|
|
|
|
RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicJavaKey)); |
|
|
|
|
string xmlpublicKey = string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>", |
|
|
|
|
Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()), |
|
|
|
|
Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned())); |
|
|
|
|
Console.WriteLine(xmlpublicKey); |
|
|
|
|
return xmlpublicKey; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endregion RES加密 |
|
|
|
|
} |
|
|
|
|
} |