无需Duende Identity Server实现Blazor WASM用户登录角色管理可行吗?
当然可以不使用Duende Identity Server
你的场景是单应用本地用户管理,只需要实现本应用内的用户存储、登录和角色分配——ASP.NET Core Identity(你已经在代码里用了AddDefaultIdentity)本身就提供了这些核心能力,完全没必要依赖Duende Identity Server(它主要是做跨应用单点登录或独立API授权服务的)。下面是具体的改造步骤:
1. 删掉Duende Identity Server相关代码
把Program.cs里这些和Duende有关的代码全部移除:
- 整个
builder.Services.AddIdentityServer().AddApiAuthorization<...>()代码块 app.UseIdentityServer()中间件builder.Services.AddAuthentication().AddIdentityServerJwt()这一行
2. 替换为JWT认证配置
在原来AddIdentityServerJwt的位置,添加JWTBearer认证的配置,同时保留你原来的Claim映射清理逻辑:
// 保留原有Claim映射清理 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role"); // 添加JWT认证 builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, // 从配置文件读取,后面会说怎么加 ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) }; }); // 支持角色授权策略(可选,根据你的需求定义) builder.Services.AddAuthorization(options => { options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin")); });
3. 添加Token生成API
Blazor WASM客户端需要通过API获取JWT Token来完成登录,所以新建一个AuthController:
[Route("api/auth")] [ApiController] public class AuthController : ControllerBase { private readonly UserManager<ApplicationUser> _userManager; private readonly IConfiguration _config; public AuthController(UserManager<ApplicationUser> userManager, IConfiguration config) { _userManager = userManager; _config = config; } [HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginRequest model) { var user = await _userManager.FindByEmailAsync(model.Email); if (user == null || !await _userManager.CheckPasswordAsync(user, model.Password)) { return Unauthorized("邮箱或密码错误"); } // 获取用户角色 var roles = await _userManager.GetRolesAsync(user); // 组装Claim,包含你原来自定义的Avatar、Nick等 var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Email, user.Email), new Claim(UserClaim.Avatar, user.Avatar), new Claim(UserClaim.Nick, user.Nick), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) }; // 添加角色Claim foreach (var role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); } // 生成Token var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _config["Jwt:Issuer"], audience: _config["Jwt:Audience"], claims: claims, expires: DateTime.Now.AddHours(3), signingCredentials: creds ); return Ok(new { Token = new JwtSecurityTokenHandler().WriteToken(token), Expiration = token.ValidTo }); } } // 登录请求模型 public class LoginRequest { public string Email { get; set; } public string Password { get; set; } }
4. 配置JWT参数到appsettings.json
在服务器项目的appsettings.json里添加JWT的密钥和 issuer/audience(密钥要设置成足够长的随机字符串,别用示例里的):
"Jwt": { "Key": "Your_Super_Long_Random_Secret_Key_Here_At_Least_32_Chars", "Issuer": "https://yourappdomain.com", "Audience": "https://yourappdomain.com" }
5. 调整Blazor客户端的认证逻辑
在客户端项目的Program.cs里,替换原来的OIDC认证为自定义JWT认证:
builder.Services.AddAuthorizationCore(); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); // 添加自定义认证服务和状态提供者 builder.Services.AddScoped<AuthService>(); builder.Services.AddScoped<CustomAuthStateProvider>(); builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<CustomAuthStateProvider>());
然后创建AuthService和CustomAuthStateProvider来处理登录、Token存储和认证状态:
public class AuthService { private readonly HttpClient _http; private readonly CustomAuthStateProvider _authStateProvider; public AuthService(HttpClient http, CustomAuthStateProvider authStateProvider) { _http = http; _authStateProvider = authStateProvider; } public async Task<bool> Login(LoginRequest model) { var response = await _http.PostAsJsonAsync("api/auth/login", model); if (!response.IsSuccessStatusCode) return false; var tokenResult = await response.Content.ReadFromJsonAsync<TokenResponse>(); if (tokenResult == null) return false; // 把Token存在本地安全存储里 await SecureStorage.SetAsync("authToken", tokenResult.Token); // 通知认证状态更新 _authStateProvider.NotifyAuthenticated(tokenResult.Token); return true; } public async Task Logout() { await SecureStorage.RemoveAsync("authToken"); _authStateProvider.NotifyLoggedOut(); } } public class CustomAuthStateProvider : AuthenticationStateProvider { public override async Task<AuthenticationState> GetAuthenticationStateAsync() { var token = await SecureStorage.GetAsync("authToken"); var identity = string.IsNullOrEmpty(token) ? new ClaimsIdentity() : new ClaimsIdentity(ParseClaimsFromToken(token), "jwt"); return new AuthenticationState(new ClaimsPrincipal(identity)); } public void NotifyAuthenticated(string token) { var identity = new ClaimsIdentity(ParseClaimsFromToken(token), "jwt"); NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new ClaimsPrincipal(identity)))); } public void NotifyLoggedOut() { var identity = new ClaimsIdentity(); NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new ClaimsPrincipal(identity)))); } private IEnumerable<Claim> ParseClaimsFromToken(string token) { var payload = token.Split('.')[1]; var jsonBytes = Convert.FromBase64String(payload.PadRight(payload.Length + (4 - payload.Length % 4) % 4, '=')); var claims = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes); return claims.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())); } } // 响应模型 public class TokenResponse { public string Token { get; set; } public DateTime Expiration { get; set; } }
最后验证功能
- 用户管理:你原来的
AppDbContext和ApplicationUser已经和ASP.NET Core Identity集成,可以直接用UserManager/RoleManager创建用户、分配角色(也可以用Identity自带的Razor Pages管理用户) - 登录:在Blazor组件里调用
AuthService.Login即可完成登录,认证状态会自动同步 - 角色授权:在组件或API控制器上用
[Authorize(Roles = "Admin")]或者自定义策略做权限验证
内容的提问来源于stack exchange,提问作者Clyde
相关产品推荐
相关产品推荐

