fix auth
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
@page "/account/login"
|
||||
@using System.Text.Json
|
||||
@layout EmptyLayout
|
||||
@inject ILogger<Login> Logger
|
||||
@inject IJSRuntime JS
|
||||
|
||||
<PageTitle>登录</PageTitle>
|
||||
|
||||
@@ -48,7 +50,6 @@ else
|
||||
</Flex>
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
string handler = "Server";
|
||||
|
||||
@@ -104,30 +105,53 @@ else
|
||||
|
||||
try
|
||||
{
|
||||
// 请求后端登录接口,后端返回 ApiResult<AuthResponse>
|
||||
var api = "/api/sign/in";
|
||||
var result = await HttpService.Post<ApiResult<AuthResponse>>(api, login);
|
||||
if (result.Success && result.Data != null)
|
||||
|
||||
if (!OperatingSystem.IsBrowser())
|
||||
{
|
||||
var auth = result.Data;
|
||||
// Server 模式:使用浏览器发起的 fetch(通过 JS)并携带 credentials: 'include'
|
||||
Logger.LogInformation("Server 模式,使用浏览器 fetch 登录");
|
||||
var jsResult = await JS.InvokeAsync<JsonElement>("__atomx_post_json", api, login);
|
||||
|
||||
// 保存 access + refresh 到 localStorage(WASM 场景)
|
||||
await localStorage.SetItemAsync("accessToken", auth.Token);
|
||||
await localStorage.SetItemAsync("refreshToken", auth.RefreshToken);
|
||||
|
||||
// 更新客户端 AuthenticationState(调用自定义 Provider 更新方法)
|
||||
if (AuthStateProvider is PersistentAuthenticationStateProvider provider)
|
||||
var success = jsResult.TryGetProperty("success", out var sprop) && sprop.GetBoolean();
|
||||
if (success && jsResult.TryGetProperty("data", out var dprop) && dprop.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
// provider 仅需要 access token 更新来触发 UI 更新
|
||||
provider.UpdateAuthenticationState(auth.Token);
|
||||
}
|
||||
var token = dprop.TryGetProperty("token", out var t) ? t.GetString() ?? string.Empty : string.Empty;
|
||||
var refresh = dprop.TryGetProperty("refreshToken", out var r) ? r.GetString() ?? string.Empty : string.Empty;
|
||||
|
||||
Logger.LogInformation("登录成功,跳转: {ReturnUrl}", ReturnUrl);
|
||||
Navigation.NavigateTo(ReturnUrl ?? "/");
|
||||
// WASM 的 localStorage 在 Server Circuit 中无意义,这里不用写 localStorage。
|
||||
// 浏览器已通过 fetch 收到 Set-Cookie;强制重载使 Circuit 使用新 Cookie。
|
||||
Logger.LogInformation("登录成功,server 跳转: {ReturnUrl}", ReturnUrl);
|
||||
Navigation.NavigateTo(ReturnUrl ?? "/", forceLoad: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = jsResult.TryGetProperty("message", out var m) ? m.GetString() ?? "登录失败" : "登录失败";
|
||||
ModalService.Error(new ConfirmOptions() { Title = "提示", Content = msg });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ModalService.Error(new ConfirmOptions() { Title = "提示", Content = result.Message });
|
||||
// Wasm 模式:继续使用 HttpService(之前逻辑),保存 localStorage 并更新 AuthStateProvider
|
||||
var result = await HttpService.Post<ApiResult<AuthResponse>>(api, login);
|
||||
if (result.Success && result.Data != null)
|
||||
{
|
||||
var auth = result.Data;
|
||||
await localStorage.SetItemAsync(StorageKeys.AccessToken, auth.Token);
|
||||
await localStorage.SetItemAsync(StorageKeys.RefreshToken, auth.RefreshToken);
|
||||
|
||||
if (AuthStateProvider is PersistentAuthenticationStateProvider provider)
|
||||
{
|
||||
provider.UpdateAuthenticationState(auth.Token);
|
||||
}
|
||||
|
||||
Logger.LogInformation("登录成功,wasm 跳转: {ReturnUrl}", ReturnUrl);
|
||||
Navigation.NavigateTo(ReturnUrl ?? "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
ModalService.Error(new ConfirmOptions() { Title = "提示", Content = result.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -149,4 +173,26 @@ else
|
||||
await LoginAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@* 页面内 JS 辅助:用于在 Server 模式下从浏览器发起 POST 并携带凭证,使浏览器接收 Set-Cookie *@
|
||||
<script>
|
||||
window.__atomx_post_json = async function (url, data) {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
const text = await res.text();
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return { success: res.ok, message: text };
|
||||
}
|
||||
} catch (err) {
|
||||
return { success: false, message: err?.toString() ?? 'network error' };
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,11 +1,76 @@
|
||||
@page "/logout"
|
||||
@layout EmptyLayout
|
||||
@inject IJSRuntime JS
|
||||
@inject ILogger<Logout> Logger
|
||||
@inject NavigationManager Navigation
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject HttpService HttpService
|
||||
@using System.Text.Json
|
||||
|
||||
@code {
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await ((PersistentAuthenticationStateProvider)AuthStateProvider).MarkUserAsLoggedOut();
|
||||
Navigation.NavigateTo("/account/login");
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
}
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!firstRender) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 如果运行在浏览器 (WASM),直接调用后端 API 并清除 localStorage / provider
|
||||
if (OperatingSystem.IsBrowser())
|
||||
{
|
||||
Logger.LogInformation("WASM logout: call API and clear local storage");
|
||||
try
|
||||
{
|
||||
await HttpService.Post<ApiResult<string>>("/api/sign/out", null);
|
||||
}
|
||||
catch { /* 忽略网络错误,仍继续清理客户端状态 */ }
|
||||
|
||||
if (AuthStateProvider is Atomx.Admin.Client.Utils.PersistentAuthenticationStateProvider provider)
|
||||
{
|
||||
await provider.MarkUserAsLoggedOut();
|
||||
}
|
||||
|
||||
Navigation.NavigateTo("/account/login");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Server 模式:通过浏览器 fetch 发起带凭据的请求以便浏览器接收并删除 Cookie,然后强制重载
|
||||
Logger.LogInformation("Server logout: use browser fetch to call /api/sign/out");
|
||||
var jsResult = await JS.InvokeAsync<JsonElement>("__atomx_post_json", "/api/sign/out", (object?)null);
|
||||
|
||||
// 尝试解析返回,忽略细节
|
||||
var success = jsResult.ValueKind == JsonValueKind.Object && jsResult.TryGetProperty("success", out var sp) && sp.GetBoolean();
|
||||
Logger.LogInformation("Server logout result: {Success}", success);
|
||||
|
||||
// 尽管我们可能已经处理了服务器态,强制重新加载确保 Circuit 更新
|
||||
Navigation.NavigateTo("/account/login", forceLoad: true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Logout failed but proceeding to login page");
|
||||
Navigation.NavigateTo("/account/login", forceLoad: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@* 页面内 JS 辅助:用于在 Server 模式下从浏览器发起 POST 并携带凭证,使浏览器接收 Set-Cookie/删除 Cookie *@
|
||||
<script>
|
||||
window.__atomx_post_json = async function (url, data) {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: data ? JSON.stringify(data) : null
|
||||
});
|
||||
const text = await res.text();
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return { success: res.ok, message: text };
|
||||
}
|
||||
} catch (err) {
|
||||
return { success: false, message: err?.toString() ?? 'network error' };
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -33,7 +33,7 @@
|
||||
帐号列表
|
||||
<div>
|
||||
<AuthorizePermissionView Permission="@Permissions.User.Create">
|
||||
<button class="btn btn-primary">创建用户</button>
|
||||
<Button Class="me-3" OnClick="OnCreateClick" Type="ButtonType.Primary">新增</Button>
|
||||
</AuthorizePermissionView>
|
||||
@* <AuthorizeView Policy="@Permissions.Admin.Edit">
|
||||
<Authorized>
|
||||
|
||||
Reference in New Issue
Block a user