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

.NET桌面应用(WPF/WinUI3)通过本地独立账户认证Web API

基于本地ASP.NET Identity的跨端认证实现方案

核心思路是搭建自定义JWT认证体系,复用本地ASP.NET Identity的用户数据,让Windows桌面应用通过用户名密码登录获取JWT令牌,后续API调用携带令牌完成身份验证,完全脱离Azure/Entra依赖。

1. 配置Web API的Identity与JWT认证

先在Web API项目中完成基础配置,集成本地Identity并启用JWT认证:

1.1 服务配置(Program.cs)

// 配置本地数据库与ASP.NET Identity
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

// 配置JWT认证参数
var jwtSettings = builder.Configuration.GetSection("JwtSettings");
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = jwtSettings["Issuer"],
            ValidAudience = jwtSettings["Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]))
        };
    });

// 启用授权
builder.Services.AddAuthorization();

// 注册中间件(注意顺序)
app.UseAuthentication();
app.UseAuthorization();

1.2 JWT配置(appsettings.json)

"JwtSettings": {
  "Issuer": "YourWebApiDomain",
  "Audience": "YourWebApiAudience",
  "SecretKey": "YourStrongSecretKeyAtLeast16Characters",
  "TokenExpirationMinutes": 60
}

2. 添加桌面应用专属登录端点

在Web API中创建认证控制器,处理桌面应用的用户名密码登录请求并生成JWT令牌:

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IConfiguration _configuration;

    public AuthController(UserManager<IdentityUser> userManager, IConfiguration configuration)
    {
        _userManager = userManager;
        _configuration = configuration;
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginRequest request)
    {
        var user = await _userManager.FindByNameAsync(request.Username);
        if (user == null || !await _userManager.CheckPasswordAsync(user, request.Password))
        {
            return Unauthorized("无效的用户名或密码");
        }

        var token = GenerateJwtToken(user);
        return Ok(new { Token = token });
    }

    private string GenerateJwtToken(IdentityUser user)
    {
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.Id),
            new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecretKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _configuration["JwtSettings:Issuer"],
            audience: _configuration["JwtSettings:Audience"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(Convert.ToDouble(_configuration["JwtSettings:TokenExpirationMinutes"])),
            signingCredentials: creds);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    public class LoginRequest
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }
}

3. Windows桌面应用实现认证与API调用

以WPF为例,通过HttpClient完成登录获取令牌,后续API请求携带令牌:

// 获取JWT令牌
private async Task<string> GetAuthToken(string username, string password)
{
    using var client = new HttpClient();
    var loginRequest = new { Username = username, Password = password };
    var json = JsonSerializer.Serialize(loginRequest);
    var content = new StringContent(json, Encoding.UTF8, "application/json");

    var response = await client.PostAsync("https://your-web-api-domain/api/auth/login", content);
    if (!response.IsSuccessStatusCode)
    {
        throw new Exception($"登录失败: {await response.Content.ReadAsStringAsync()}");
    }

    var result = await response.Content.ReadFromJsonAsync<LoginResult>();
    return result.Token;
}

// 调用受保护的API
private async Task CallProtectedApi(string token)
{
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    var response = await client.GetAsync("https://your-web-api-domain/api/protected-resource");
    if (response.IsSuccessStatusCode)
    {
        var data = await response.Content.ReadAsStringAsync();
        // 处理返回数据
    }
    else
    {
        // 处理未授权或请求错误
    }
}

// 辅助模型
public class LoginResult
{
    public string Token { get; set; }
}

4. 额外优化建议

  • 令牌刷新:添加刷新令牌机制,生成JWT时同步生成刷新令牌存入数据库,桌面应用可通过刷新令牌获取新的访问令牌,避免频繁登录。
  • 传输安全:确保所有请求使用HTTPS,防止密码和令牌明文泄露。
  • 权限控制:在JWT中添加角色/自定义声明,API端通过[Authorize(Roles = "Admin")]或自定义策略实现细粒度权限控制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 05:07:12