FixAuth
This commit is contained in:
@@ -24,9 +24,9 @@ namespace TechHelper.Client.Exam
|
||||
|
||||
public class QuestionAnswerStatus
|
||||
{
|
||||
public string QuestionSequence { get; set; } = string.Empty; // 题目序号,例如 "1.1"
|
||||
public string QuestionText { get; set; } = string.Empty; // 题目文本
|
||||
public float QuestionScore { get; set; } // 题目分值
|
||||
public string QuestionSequence { get; set; } = string.Empty; // 题目序号,例如 "1.1"
|
||||
public string QuestionText { get; set; } = string.Empty; // 题目文本
|
||||
public float QuestionScore { get; set; } // 题目分值
|
||||
public Dictionary<Guid, bool> StudentCorrectStatus { get; set; } = new Dictionary<Guid, bool>();
|
||||
// Key: Student.Id, Value: true 表示正确,false 表示错误
|
||||
}
|
||||
|
@@ -1,36 +0,0 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace TechHelper.Client.Exam.QuestionSimple
|
||||
{
|
||||
|
||||
[XmlRoot("QG")]
|
||||
public class QuestionGroup
|
||||
{
|
||||
[XmlElement("T")]
|
||||
public string Title { get; set; }
|
||||
[XmlElement("QR")]
|
||||
public string QuestionReference { get; set; } = "";
|
||||
[XmlArray("SQs")]
|
||||
[XmlArrayItem("SQ")]
|
||||
public List<SubQuestion> SubQuestions { get; set; } = new List<SubQuestion>();
|
||||
[XmlArray("SQGs")]
|
||||
[XmlArrayItem("QG")]
|
||||
public List<QuestionGroup> SubQuestionGroups { get; set; } = new List<QuestionGroup>();
|
||||
}
|
||||
public class SubQuestion
|
||||
{
|
||||
[XmlElement("T")]
|
||||
public string Stem { get; set; }
|
||||
[XmlArray("Os")]
|
||||
[XmlArrayItem("O")]
|
||||
public List<Option> Options { get; set; } = new List<Option>();
|
||||
[XmlElement("SA")]
|
||||
public string SampleAnswer { get; set; } = "";
|
||||
}
|
||||
public class Option
|
||||
{
|
||||
[XmlAttribute("V")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
}
|
@@ -1,19 +1,19 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using TechHelper.Client.HttpRepository;
|
||||
using System.Net.Http.Headers;
|
||||
using TechHelper.Client.HttpRepository;
|
||||
|
||||
|
||||
namespace BlazorProducts.Client.HttpInterceptor
|
||||
{
|
||||
public class HttpInterceptorHandlerService : DelegatingHandler
|
||||
{
|
||||
private readonly NavigationManager _navManager;
|
||||
private readonly RefreshTokenService2 _refreshTokenService;
|
||||
private readonly NavigationManager _navManager;
|
||||
private readonly IRefreshTokenService _refreshTokenService;
|
||||
|
||||
public HttpInterceptorHandlerService(
|
||||
NavigationManager navManager,
|
||||
RefreshTokenService2 refreshTokenService)
|
||||
IRefreshTokenService refreshTokenService)
|
||||
{
|
||||
_navManager = navManager;
|
||||
_refreshTokenService = refreshTokenService;
|
||||
@@ -25,15 +25,14 @@ namespace BlazorProducts.Client.HttpInterceptor
|
||||
|
||||
if (absolutePath != null && !absolutePath.Contains("token") && !absolutePath.Contains("account"))
|
||||
{
|
||||
var token = await _refreshTokenService.TryRefreshToken();
|
||||
var token = await _refreshTokenService.TryRefreshToken();
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var response = await base.SendAsync(request, cancellationToken);
|
||||
|
||||
await HandleResponse(response);
|
||||
@@ -45,17 +44,17 @@ namespace BlazorProducts.Client.HttpInterceptor
|
||||
{
|
||||
if (response is null)
|
||||
{
|
||||
|
||||
|
||||
_navManager.NavigateTo("/error");
|
||||
throw new HttpResponseException("服务器不可用。");
|
||||
throw new HttpResponseException("服务器不可用。");
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
|
||||
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"HTTP 错误: {response.StatusCode}. 详情: {errorContent}");
|
||||
Console.WriteLine($"HTTP 错误: {response.StatusCode}. 详情: {errorContent}");
|
||||
|
||||
switch (response.StatusCode)
|
||||
{
|
||||
@@ -67,8 +66,15 @@ namespace BlazorProducts.Client.HttpInterceptor
|
||||
// 400 Bad Request error. Often, you don't navigate for this; you display validation errors on the UI.
|
||||
break;
|
||||
case HttpStatusCode.Unauthorized:
|
||||
// 401 Unauthorized error. Navigate to an unauthorized page or login page.
|
||||
_navManager.NavigateTo("/unauthorized"); // Or: _navManager.NavigateTo("/authentication/login");
|
||||
var token = await _refreshTokenService.TryRefreshToken();
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
_navManager.NavigateTo(_navManager.Uri, forceLoad: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_navManager.NavigateTo("/unauthorized");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// For all other errors, navigate to a general error page.
|
||||
|
@@ -20,13 +20,12 @@ namespace TechHelper.Client.HttpRepository
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly NavigationManager _navigationManager;
|
||||
|
||||
// 构造函数现在直接接收 HttpClient
|
||||
public AuthenticationClientService(HttpClient client, // <-- 修正点:直接注入 HttpClient
|
||||
public AuthenticationClientService(HttpClient client,
|
||||
AuthenticationStateProvider authenticationStateProvider,
|
||||
ILocalStorageService localStorageService,
|
||||
NavigationManager navigationManager)
|
||||
{
|
||||
_client = client; // <-- 修正点:直接赋值
|
||||
_client = client;
|
||||
_localStorageService = localStorageService;
|
||||
_stateProvider = authenticationStateProvider;
|
||||
_navigationManager = navigationManager;
|
||||
@@ -34,8 +33,6 @@ namespace TechHelper.Client.HttpRepository
|
||||
|
||||
public async Task<AuthResponseDto> LoginAsync(UserForAuthenticationDto userForAuthenticationDto)
|
||||
{
|
||||
// 移除 using (_client = _clientFactory.CreateClient("Default"))
|
||||
// _client 已经是注入的实例,直接使用它
|
||||
var reponse = await _client.PostAsJsonAsync("account/login",
|
||||
userForAuthenticationDto);
|
||||
|
||||
@@ -71,7 +68,6 @@ namespace TechHelper.Client.HttpRepository
|
||||
|
||||
public async Task<string> RefreshTokenAsync()
|
||||
{
|
||||
// 移除 using (_client = _clientFactory.CreateClient("Default"))
|
||||
var token = _localStorageService.GetItem<string>("authToken");
|
||||
var refreshToken = _localStorageService.GetItem<string>("refreshToken");
|
||||
|
||||
@@ -167,6 +163,9 @@ namespace TechHelper.Client.HttpRepository
|
||||
((AuthStateProvider)_stateProvider).NotifyUserAuthentication(
|
||||
result.Token);
|
||||
|
||||
|
||||
|
||||
|
||||
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(
|
||||
"bearer", result.Token);
|
||||
|
||||
|
@@ -24,16 +24,15 @@ namespace TechHelper.Client.HttpRepository
|
||||
var authState = await _authenticationStateProvider.Value.GetAuthenticationStateAsync();
|
||||
var user = authState.User;
|
||||
|
||||
// 如果 user 或 claims 为空,表示用户未认证,直接返回空字符串
|
||||
if (user?.Identity == null || !user.Identity.IsAuthenticated)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var expClaim = user.FindFirst(c => c.Type.Equals("exp"))?.Value; // 使用 ?. 防止空引用
|
||||
var expClaim = user.FindFirst(c => c.Type.Equals("exp"))?.Value;
|
||||
if (string.IsNullOrEmpty(expClaim))
|
||||
{
|
||||
return string.Empty; // 没有过期时间声明,也直接返回
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var expTime = DateTimeOffset.FromUnixTimeSeconds(
|
||||
@@ -41,9 +40,11 @@ namespace TechHelper.Client.HttpRepository
|
||||
|
||||
var diff = expTime - DateTime.UtcNow;
|
||||
|
||||
// 只有当令牌即将过期时才尝试刷新
|
||||
|
||||
var n = DateTime.UtcNow;
|
||||
|
||||
if (diff.TotalMinutes <= 2)
|
||||
return await _authenticationClientService.Value.RefreshTokenAsync(); // 访问 .Value 来调用方法
|
||||
return await _authenticationClientService.Value.RefreshTokenAsync();
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
@@ -1,46 +0,0 @@
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace TechHelper.Client.HttpRepository
|
||||
{
|
||||
public class RefreshTokenService2
|
||||
{
|
||||
private readonly Lazy<AuthenticationStateProvider> _authenticationStateProvider;
|
||||
private readonly Lazy<IAuthenticationClientService> _authenticationClientService;
|
||||
|
||||
public RefreshTokenService2(IServiceProvider serviceProvider)
|
||||
{
|
||||
_authenticationStateProvider = new Lazy<AuthenticationStateProvider>(
|
||||
() => serviceProvider.GetRequiredService<AuthenticationStateProvider>());
|
||||
|
||||
_authenticationClientService = new Lazy<IAuthenticationClientService>(
|
||||
() => serviceProvider.GetRequiredService<IAuthenticationClientService>());
|
||||
}
|
||||
|
||||
public async Task<string> TryRefreshToken()
|
||||
{
|
||||
var authState = await _authenticationStateProvider.Value.GetAuthenticationStateAsync();
|
||||
var user = authState.User;
|
||||
|
||||
if (user?.Identity == null || !user.Identity.IsAuthenticated)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var expClaim = user.FindFirst(c => c.Type.Equals("exp"))?.Value;
|
||||
if (string.IsNullOrEmpty(expClaim))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var expTime = DateTimeOffset.FromUnixTimeSeconds(
|
||||
Convert.ToInt64(expClaim));
|
||||
|
||||
var diff = expTime - DateTime.UtcNow;
|
||||
|
||||
if (diff.TotalMinutes <= 2)
|
||||
return await _authenticationClientService.Value.RefreshTokenAsync();
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
10
TechHelper.Client/Pages/Exam/AssignmentDetailView.razor
Normal file
10
TechHelper.Client/Pages/Exam/AssignmentDetailView.razor
Normal file
@@ -0,0 +1,10 @@
|
||||
<MudText> QuestionNum </MudText>
|
||||
<MudText> QuestionTypeDis </MudText>
|
||||
<MudText> ErrorQuestionTypeDis </MudText>
|
||||
<MudText> ErrorLessonDis </MudText>
|
||||
<MudText> ErrorIndexDis </MudText>
|
||||
<MudText> ErrorNum </MudText>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
9
TechHelper.Client/Pages/Exam/QuestionDetailView.razor
Normal file
9
TechHelper.Client/Pages/Exam/QuestionDetailView.razor
Normal file
@@ -0,0 +1,9 @@
|
||||
<MudText> Title </MudText>
|
||||
<MudText> Error Person Num </MudText>
|
||||
<MudText> List Error Person </MudText>
|
||||
<MudText> Lesson </MudText>
|
||||
<MudText> Keypoint </MudText>
|
||||
<MudText> Answer </MudText>
|
||||
@code {
|
||||
|
||||
}
|
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Components.Authorization;
|
||||
using MudBlazor;
|
||||
using System.Collections.Frozen;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Claims;
|
||||
using TechHelper.Client.HttpRepository;
|
||||
using TechHelper.Client.Services;
|
||||
|
||||
|
@@ -44,7 +44,6 @@ builder.Services.AddScoped<IRefreshTokenService, RefreshTokenService>();
|
||||
builder.Services.AddScoped<IClassServices, ClasssServices>();
|
||||
builder.Services.AddScoped<IEmailSender, QEmailSender>();
|
||||
builder.Services.AddScoped<HttpInterceptorHandlerService>();
|
||||
builder.Services.AddScoped<RefreshTokenService2>();
|
||||
builder.Services.AddScoped<IAIService, AiService>();
|
||||
builder.Services.AddHttpClient("WebApiClient", client =>
|
||||
{
|
||||
|
@@ -1,15 +1,18 @@
|
||||
using Entities.DTO;
|
||||
using System.Net.Http.Json;
|
||||
using System.Net.Http.Json;
|
||||
using TechHelper.Client.HttpRepository;
|
||||
|
||||
namespace TechHelper.Client.Services
|
||||
{
|
||||
public class ClasssServices : IClassServices
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
private readonly IAuthenticationClientService _authenticationClientService;
|
||||
|
||||
public ClasssServices(HttpClient client)
|
||||
public ClasssServices(HttpClient client, IAuthenticationClientService authenticationClientService)
|
||||
{
|
||||
_client = client;
|
||||
_authenticationClientService = authenticationClientService;
|
||||
}
|
||||
|
||||
public Task<ResponseDto> CreateClass(UserRegistrationToClassDto userClass)
|
||||
@@ -25,6 +28,8 @@ namespace TechHelper.Client.Services
|
||||
userRegistrationToClassDto);
|
||||
var data = await result.Content.ReadAsStringAsync();
|
||||
|
||||
await _authenticationClientService.RefreshTokenAsync();
|
||||
|
||||
return new ResponseDto
|
||||
{
|
||||
IsSuccessfulRegistration = result.IsSuccessStatusCode,
|
||||
|
@@ -9,13 +9,13 @@ namespace TechHelper.Client.Services
|
||||
{
|
||||
public class ExamService : IExamService
|
||||
{
|
||||
private readonly IAIService _aIService; // 遵循命名规范,字段前加下划线
|
||||
private readonly HttpClient _client; // 直接注入 HttpClient
|
||||
private readonly IAIService _aIService;
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public ExamService(IAIService aIService, HttpClient client) // 修正点:直接注入 HttpClient
|
||||
public ExamService(IAIService aIService, HttpClient client)
|
||||
{
|
||||
_aIService = aIService;
|
||||
_client = client; // 赋值注入的 HttpClient 实例
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public ApiResponse ConvertToXML<T>(string xmlContent)
|
||||
|
Reference in New Issue
Block a user