Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing

This commit is contained in:
he
2024-11-04 10:41:01 +08:00
15 changed files with 18589 additions and 69 deletions
@@ -1757,8 +1757,8 @@ namespace IRaCIS.Core.Application.Service.Common
var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
//自身一致性分析
.WhereIf(inQuery.ReadingExportType == ExportResult.SelfAnalysis, t => t.IsSelfAnalysis == true || t.IsSelfAnalysis == null)
.WhereIf(inQuery.ReadingExportType == ExportResult.GroupAnalysis, t => t.IsSelfAnalysis == false || t.IsSelfAnalysis == null)
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == true || t.IsSelfAnalysis == null)
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfInterReaderAnalysisResults, t => t.IsSelfAnalysis == false || t.IsSelfAnalysis == null)
//访视和全局查询已签名完成的,裁判可以是未签名,未完成的
.Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge)
@@ -1859,7 +1859,7 @@ namespace IRaCIS.Core.Application.Service.Common
var export_Template = StaticData.Export.TrialSelfAnalysisList_Export;
if (inQuery.ReadingExportType == ExportResult.SelfAnalysis)
if (inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults)
{
//找到非一致性分析的任务
var selfExportList = list.Where(t => t.IsSelfAnalysis == null).ToList();
@@ -1956,7 +1956,7 @@ namespace IRaCIS.Core.Application.Service.Common
};
var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(CommonEvaluationExport), criterion.CriterionType, dynamicColumnConfig);
var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(AnalysisDynamicCommonExport), criterion.CriterionType, dynamicColumnConfig);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
@@ -325,7 +325,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
if (findStudy.SubejectCode == inCommand.SubjectCode && findStudy.SubjectId == inCommand.SubjectId)
{
//上传给后处理其他访视了
if (inCommand.VisitTaskId != null && findStudy.VisitTaskId != null && findStudy.VisitTaskId != inCommand.VisitTaskId)
if (inCommand.VisitTaskId != null && findStudy.VisitTaskId != null && findStudy.VisitTaskId != inCommand.VisitTaskId && findStudy.SubjectVisitId != currentUploadSubjectVisitId)
{
result.Add(new TaskStudyArchiveConfirmResult() { StudyInstanceUid = studyUid, IsAllowReUpload = false, IsAllowUpload = false });
}
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace IRaCIS.Core.Application.Service.OAuth;
@@ -8,30 +9,35 @@ public class LogtoTokenResponse
/// The access token issued by the Logto authorization server.
/// </summary>
[JsonPropertyName("access_token")]
[JsonProperty("access_token")]
public string AccessToken { get; set; } = null!;
/// <summary>
/// The type of the token issued by the Logto authorization server.
/// </summary>
[JsonPropertyName("token_type")]
[JsonProperty("token_type")]
public string TokenType { get; set; } = null!;
/// <summary>
/// The lifetime in seconds of the access token.
/// </summary>
[JsonPropertyName("expires_in")]
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
/// <summary>
/// The refresh token, which can be used to obtain new access tokens using the same authorization grant.
/// </summary>
[JsonPropertyName("refresh_token")]
[JsonProperty("refresh_token")]
public string? RefreshToken { get; set; } = null!;
/// <summary>
/// The ID token, which can be used to verify the identity of the user.
/// </summary>
[JsonPropertyName("id_token")]
[JsonProperty("id_token")]
public string? IdToken { get; set; } = null;
}
@@ -1,11 +1,20 @@
using IdentityModel.Client;
using Azure.Core;
using IdentityModel;
using IdentityModel.Client;
using IRaCIS.Core.Application.Service.OAuth;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPOI.SS.Formula.Functions;
using Org.BouncyCastle.Utilities.Net;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
@@ -16,6 +25,360 @@ namespace IRaCIS.Core.Application.Service
{
#region authorization_code PKCE
public IResponseOutput TestPCKEOrgin()
{
// 1. 生成 code_verifier 和 code_challenge
string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
string codeChallenge = PkceUtil.GenerateCodeChallenge(codeVerifier);
Console.WriteLine(codeVerifier);
string clientId = "aj34vqrpvz8olsbxwtcog";
string redirectUri = "http://localhost:6100/OAuth/TestPKCECallBack";
string state = "123456";
// 构造请求的 URL
string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
$"?client_id={clientId}" +
$"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
$"&response_type=code" +
$"&scope=openid profile email phone" +
$"&code_challenge={codeChallenge}" +
$"&code_challenge_method=S256" +
$"&state={state}";
Console.WriteLine("请将以下 URL 复制到浏览器中,完成登录后获取 code:");
Console.WriteLine(authorizationUrl);
return ResponseOutput.Ok();
}
[AllowAnonymous]
[RoutePattern(HttpMethod = "Get")]
public async Task<IResponseOutput> TestPKCECallBackAsync(string code)
{
string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
// OIDC 配置,替换为您的 OIDC 提供者的配置
string tokenEndpoint = "https://logto.test.extimaging.com/oidc/token"; // 替换为实际 token 端点
string clientId = "aj34vqrpvz8olsbxwtcog";
string redirectUri = "http://localhost:6100/OAuth/TestPKCECallBack"; // 替换为前端的回调 URL
var baseUrl = "https://logto.test.extimaging.com";
var opts = new RestClientOptions(baseUrl);
using var client = new RestClient(opts);
//https://blog.logto.io/troubleshoot-invalid-grant-error/
var request = new RestRequest("oidc/token", Method.Post)
.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "authorization_code")
.AddParameter("code", code)
.AddParameter("redirect_uri", redirectUri)
.AddParameter("client_id", clientId)
.AddParameter("code_verifier", codeVerifier); // 使用 PKCE
// 发送请求并获取响应
var response = await client.ExecuteAsync<LogtoTokenResponse>(request);
if (response.StatusCode == HttpStatusCode.OK)
{
var tokenResponse = response.Data;
Console.WriteLine(tokenResponse.ToJsonStr());
var userInfoRequest = new RestRequest($"oidc/me", Method.Get)
.AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}");
var userResponse = await client.ExecuteAsync(userInfoRequest);
Console.WriteLine(userResponse.Content);
}
return ResponseOutput.Ok();
}
#endregion
#region authorization_code OidcClient PKCE
[AllowAnonymous]
[RoutePattern(HttpMethod = "Get")]
public IResponseOutput TestPKCE()
{
// 1. 生成 code_verifier 和 code_challenge
string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
string codeChallenge = PkceUtil.GenerateCodeChallenge(codeVerifier);
Console.WriteLine(codeVerifier);
string clientId = "aj34vqrpvz8olsbxwtcog";
string redirectUri = "http://localhost:6100/OAuth/TestOidcClientPKCECallBack";
string state = "123456";
// 构造请求的 URL
string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
$"?client_id={clientId}" +
$"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
$"&response_type=code" +
$"&scope=openid profile email phone" +
$"&code_challenge={codeChallenge}" +
$"&code_challenge_method=S256" +
$"&state={state}";
Console.WriteLine("请将以下 URL 复制到浏览器中,完成登录后获取 code:");
Console.WriteLine(authorizationUrl);
return ResponseOutput.Ok();
}
[AllowAnonymous]
[RoutePattern(HttpMethod = "Get")]
public async Task<IResponseOutput> TestOidcClientPKCECallBackAsync(string code)
{
//使用IdentityModel.OidcClient 测试
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://logto.test.extimaging.com/oidc");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
}
// OIDC 配置,替换为您的 OIDC 提供者的配置
string clientId = "aj34vqrpvz8olsbxwtcog";
string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
string redirectUri = "http://localhost:6100/OAuth/TestOidcClientPKCECallBack"; // 替换为前端的回调 URL
var requestBody = new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "code", code },
{ "redirect_uri", redirectUri },
{ "client_id", clientId },
{ "code_verifier", codeVerifier } // 使用 PKCE
};
var _httpClient = new HttpClient();
var content = new FormUrlEncodedContent(requestBody);
// 发出 token 请求
var response = await _httpClient.PostAsync(disco.TokenEndpoint, content);
if (response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync();
// 解析 JSON
var jsonObject = JObject.Parse(responseBody);
// 格式化并输出 JSON
var formattedJson = jsonObject.ToString(Formatting.Indented);
Console.WriteLine(formattedJson);
var tokenResponse=JsonConvert.DeserializeObject<LogtoTokenResponse>(responseBody);
Console.WriteLine(tokenResponse);
}
else
{
var errorContent = await response.Content.ReadAsStringAsync();
throw new Exception($"Error: {errorContent}");
}
#region Secret
//// 准备请求内容
//var tokenRequest = new AuthorizationCodeTokenRequest
//{
// Address = disco.TokenEndpoint,
// ClientId = clientId,
// Code = code,
// RedirectUri = redirectUri,
// GrantType = "authorization_code",
// CodeVerifier = codeVerifier
//};
//var tokenResponse = await _httpClient.RequestTokenAsync(tokenRequest);
//if (tokenResponse.HttpStatusCode == HttpStatusCode.OK)
//{
// var apiClient = new HttpClient();
// apiClient.SetBearerToken(tokenResponse.AccessToken);
// var response = await apiClient.GetAsync(disco.UserInfoEndpoint);
// if (!response.IsSuccessStatusCode)
// {
// Console.WriteLine(response.StatusCode);
// Console.WriteLine(response.ReasonPhrase);
// }
// else
// {
// var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
// Console.WriteLine(JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
// //获取刷新token
// var refreshClient = new HttpClient();
// var refreshRequest = new RefreshTokenRequest
// {
// Address = disco.TokenEndpoint,
// ClientId = clientId,
// RefreshToken = tokenResponse.RefreshToken,
// };
// var refreshResponse = await refreshClient.RequestRefreshTokenAsync(refreshRequest);
// if (refreshResponse.IsError)
// {
// Console.WriteLine($"Error: {refreshResponse.Error}");
// }
// else
// {
// Console.WriteLine("获取刷新token 完成");
// Console.WriteLine("AccessToken:" + refreshResponse.AccessToken);
// Console.WriteLine("RefreshToken:" + refreshResponse.RefreshToken);
// }
// }
//}
#endregion
return ResponseOutput.Ok();
}
#endregion
#region OIDC authorization_code with client_secret
[AllowAnonymous]
public IResponseOutput TestOidcClientWithSecret()
{
string clientId = "tl42rjin7obxtwqqgvkti";
string redirectUri = "http://localhost:6100/OAuth/TestOidcClientCallBack";
string state = "123456";
// 构造请求的 URL
string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
$"?client_id={clientId}" +
$"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
$"&response_type=code" +
$"&scope=openid profile email phone" +
$"&state={state}";
Console.WriteLine(authorizationUrl);
return ResponseOutput.Ok();
}
[AllowAnonymous]
[RoutePattern(HttpMethod = "Get")]
public async Task<IResponseOutput> TestOidcClientCallBackAsync(string code)
{
//使用IdentityModel.OidcClient 测试
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://logto.test.extimaging.com/oidc");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
}
// OIDC 配置,替换为您的 OIDC 提供者的配置
string clientId = "tl42rjin7obxtwqqgvkti";
string clientSecret = "Pu9ig4rz44aLlxb0yKUaOiZaFk6Bcu51";
string redirectUri = "http://localhost:6100/OAuth/TestOidcClientCallBack"; // 替换为前端的回调 URL
// 准备请求内容
var tokenRequest = new AuthorizationCodeTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = clientId,
ClientSecret = clientSecret,
Code = code,
RedirectUri = redirectUri,
GrantType = "authorization_code",
};
var _httpClient = new HttpClient();
// 发出 token 请求
var tokenResponse = await _httpClient.RequestAuthorizationCodeTokenAsync(tokenRequest);
if (tokenResponse.HttpStatusCode == HttpStatusCode.OK)
{
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync(disco.UserInfoEndpoint);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
Console.WriteLine(response.ReasonPhrase);
}
else
{
var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
//获取刷新token
var refreshClient = new HttpClient();
var refreshRequest = new RefreshTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = clientId,
ClientSecret = clientSecret,
RefreshToken = tokenResponse.RefreshToken,
};
var refreshResponse = await refreshClient.RequestRefreshTokenAsync(refreshRequest);
if (refreshResponse.IsError)
{
Console.WriteLine($"Error: {refreshResponse.Error}");
}
else
{
Console.WriteLine("获取刷新token 完成");
Console.WriteLine("AccessToken:" + refreshResponse.AccessToken);
Console.WriteLine("RefreshToken:" + refreshResponse.RefreshToken);
}
}
}
return ResponseOutput.Ok();
}
#endregion
#region
/// <summary>
/// 测试客户端凭证代码
/// </summary>
@@ -66,7 +429,7 @@ namespace IRaCIS.Core.Application.Service
else
{
var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
Console.WriteLine(JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
}
}
@@ -142,5 +505,37 @@ namespace IRaCIS.Core.Application.Service
return ResponseOutput.Ok();
}
#endregion
}
public static class PkceUtil
{
// 生成 code_verifier
public static string GenerateCodeVerifier()
{
var bytes = new byte[64];
using (var random = RandomNumberGenerator.Create())
{
random.GetBytes(bytes);
return Convert.ToBase64String(bytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
}
}
// 生成 code_challenge
public static string GenerateCodeChallenge(string codeVerifier)
{
using (var sha256 = SHA256.Create())
{
var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
return Convert.ToBase64String(bytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
}
}
}
}