You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 7 WPF应用中如何处理AuthenticationState及用户信息存储?

.NET 7 WPF中存储与访问登录用户信息的方案

关于Thread.CurrentPrincipal的局限性

你当前使用的Thread.CurrentPrincipal和AppDomain.CurrentDomain.SetThreadPrincipal存在明显局限:

  • 仅作用于当前线程,WPF的多线程模型(UI线程、后台线程)下,跨线程访问会出现不一致问题
  • 应用重启后状态丢失,无法持久化登录状态
  • 无内置状态变更通知机制,无法自动同步UI(比如登录后切换菜单权限)

推荐实现方案

WPF没有ASP.NET Core那样内置的AuthenticationStateProvider,通常需要自行实现全局认证状态管理服务,结合持久化存储来处理。以下是具体实现步骤:

1. 实现全局认证状态服务

创建一个单例或可注入的服务类,管理用户ClaimsPrincipal和JWT令牌,同时提供状态变更通知:

using System.ComponentModel;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using System.Runtime.CompilerServices;

public class AuthStateManager : INotifyPropertyChanged
{
    private ClaimsPrincipal _currentUser = new ClaimsPrincipal(new ClaimsIdentity());
    private string _jwtToken;

    public ClaimsPrincipal CurrentUser
    {
        get => _currentUser;
        set
        {
            _currentUser = value;
            OnPropertyChanged();
        }
    }

    public string JwtToken
    {
        get => _jwtToken;
        set
        {
            _jwtToken = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // 从JWT解析并加载用户声明
    public void LoadUserFromToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var jwtToken = tokenHandler.ReadJwtToken(token);
        var identity = new ClaimsIdentity(jwtToken.Claims, "Jwt");
        CurrentUser = new ClaimsPrincipal(identity);
        JwtToken = token;
    }

    // 注销用户,清空状态
    public void Logout()
    {
        CurrentUser = new ClaimsPrincipal(new ClaimsIdentity());
        JwtToken = null;
    }
}

在App.xaml.cs中注册为单例(或用依赖注入容器注册):

public partial class App : Application
{
    public static AuthStateManager AuthState { get; } = new AuthStateManager();

    // 若使用依赖注入,可替换为:
    // protected override void OnStartup(StartupEventArgs e)
    // {
    //     var services = new ServiceCollection();
    //     services.AddSingleton<AuthStateManager>();
    //     // 其他服务注册...
    //     base.OnStartup(e);
    // }
}

2. 加密持久化JWT令牌

为实现重启后自动登录,需将JWT令牌加密存储到本地,推荐使用ProtectedData(系统级加密,需引用System.Security.Cryptography):

using System.Security.Cryptography;
using System.Text;

public static class TokenStorage
{
    private const string TokenKey = "UserJwtToken";
    // 应用专属熵值,增强加密安全性
    private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("YourAppUniqueEntropyKey");

    public static void SaveToken(string token)
    {
        byte[] tokenBytes = Encoding.UTF8.GetBytes(token);
        byte[] encryptedBytes = ProtectedData.Protect(tokenBytes, Entropy, DataProtectionScope.CurrentUser);
        Properties.Settings.Default[TokenKey] = Convert.ToBase64String(encryptedBytes);
        Properties.Settings.Default.Save();
    }

    public static string GetToken()
    {
        var encryptedBase64 = Properties.Settings.Default[TokenKey] as string;
        if (string.IsNullOrEmpty(encryptedBase64))
            return null;

        byte[] encryptedBytes = Convert.FromBase64String(encryptedBase64);
        byte[] tokenBytes = ProtectedData.Unprotect(encryptedBytes, Entropy, DataProtectionScope.CurrentUser);
        return Encoding.UTF8.GetString(tokenBytes);
    }

    public static void ClearToken()
    {
        Properties.Settings.Default[TokenKey] = null;
        Properties.Settings.Default.Save();
    }
}

3. 整合到登录流程

修改你的LoginButton_Click方法,替换为全局状态管理+持久化逻辑:

private async void LoginButton_Click(object sender, RoutedEventArgs e)
{
    var client = _httpClientFactory.CreateClient();
    var tokenResponse = await client.PostAsJsonAsync("https://localhost:5001/api/login/pos", 
        new LoginCredentials(UsernameTextBox.Text, PasswordBox.Password));

    if (tokenResponse.IsSuccessStatusCode)
    {
        var loginResult = await tokenResponse.Content.ReadFromJsonAsync<LoginResult>();
        var token = loginResult.Token;
        
        // 更新全局认证状态
        App.AuthState.LoadUserFromToken(token);
        // 持久化令牌
        TokenStorage.SaveToken(token);
        
        // 这里添加登录成功后的页面跳转逻辑
        // new MainDashboard().Show();
        // Close();
    }
    else
    {
        MessageBox.Show(await tokenResponse.Content.ReadAsStringAsync(), "登录失败");
    }
}

4. 应用启动时自动恢复登录

在App.xaml.cs的OnStartup方法中添加自动登录逻辑:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    
    var savedToken = TokenStorage.GetToken();
    if (!string.IsNullOrEmpty(savedToken))
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        if (tokenHandler.CanReadToken(savedToken))
        {
            var jwtToken = tokenHandler.ReadJwtToken(savedToken);
            // 验证令牌是否未过期
            if (jwtToken.ValidTo > DateTime.UtcNow)
            {
                App.AuthState.LoadUserFromToken(savedToken);
                // 直接跳转到主页面
                // new MainDashboard().Show();
                // return;
            }
            else
            {
                TokenStorage.ClearToken();
            }
        }
    }
    
    // 显示登录页面
    new MainWindow().Show();
}

5. UI绑定与状态同步

在XAML中直接绑定AuthState的属性,实现UI自动更新:

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>

<!-- 显示用户名 -->
<TextBlock Text="{Binding CurrentUser.Identity.Name}" 
           DataContext="{x:Static local:App.AuthState}" />

<!-- 仅在登录后显示注销按钮 -->
<Button Content="注销" 
        Click="LogoutButton_Click"
        Visibility="{Binding CurrentUser.Identity.IsAuthenticated, Converter={StaticResource BooleanToVisibilityConverter}}" />

注销按钮逻辑:

private void LogoutButton_Click(object sender, RoutedEventArgs e)
{
    App.AuthState.Logout();
    TokenStorage.ClearToken();
    // 跳转到登录页面
    // new MainWindow().Show();
    // Close();
}

总结

WPF没有内置的认证状态管理机制,自行实现全局AuthStateManager服务+加密持久化的方案,可满足:

  • 全局统一访问用户信息
  • 跨线程状态一致性(可按需添加线程安全处理)
  • 持久化登录状态
  • 状态变更自动同步UI

内容的提问来源于stack exchange,提问作者jeb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 22:54:55