如何在Asp.NET Core 2中用AspNetCore.Identity实现带自定义角色的Windows认证
嘿,这个需求完全可以靠ASP.NET Core Identity的原生能力搞定,不用折腾复杂的自定义逻辑,我给你梳理几个关键步骤,照着来就行:
1. 先把Identity基础配置做好
首先确保你在Startup.cs里正确配置了Identity,用默认的或者自定义的用户/角色实体都可以(自定义的话扩展性更强)。比如:
services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders();
这里ApplicationUser是你继承自IdentityUser的自定义用户类,ApplicationDbContext要继承IdentityDbContext<ApplicationUser, IdentityRole, string>,Code First会自动帮你生成用户、角色、用户角色关联这些表,不用手动建表。
2. 自动给无角色用户分配"Member"角色
最省心的方式是在用户登录后自动检查角色,或者在用户首次创建时直接分配。因为你禁用了匿名登录,用户每次登录都能触发这个检查,给你两种方案选:
方案一:在登录Action里直接处理
如果你是用默认的登录页面,在登录成功的逻辑里加一段角色检查:
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); if (result.Succeeded) { var user = await _userManager.FindByEmailAsync(model.Email); // 看看用户有没有任何角色 var userRoles = await _userManager.GetRolesAsync(user); if (!userRoles.Any()) { // 没有的话就加Member角色 await _userManager.AddToRoleAsync(user, "Member"); } // 正常跳转就行 return RedirectToLocal(returnUrl); }
这种方式简单直接,适合只有密码登录的场景。
方案二:用认证事件全局处理(更通用)
如果你的系统还有外部登录(比如Google、Facebook),不想在每个登录相关的Action里重复写代码,可以用认证中间件的事件来全局处理:
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ExternalScheme; }) .AddCookie(options => { options.Events.OnPostAuthenticateAsync = async context => { var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>(); var currentUser = await userManager.GetUserAsync(context.Principal); if (currentUser != null) { var roles = await userManager.GetRolesAsync(currentUser); if (!roles.Any()) { await userManager.AddToRoleAsync(currentUser, "Member"); // 顺便更新当前请求的Claims,让这次登录就能用上新角色 var newClaims = await userManager.GetClaimsAsync(currentUser); var identity = context.Principal.Identity as ClaimsIdentity; identity.AddClaims(newClaims); } } return Task.CompletedTask; }; });
不管用户用哪种方式登录,这个事件都会触发,自动检查并分配角色,一次配置全局生效,更省心。
3. 自定义角色管理(完全替代AD组)
既然不用AD组,咱们直接用Identity的角色系统就行:
- 先初始化一些常用角色,比如Admin、Member、Editor,可以写个种子数据初始化方法:
public static async Task SeedRoles(IServiceProvider serviceProvider) { var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>(); var roles = new[] { "Admin", "Member", "Editor" }; foreach (var roleName in roles) { if (!await roleManager.RoleExistsAsync(roleName)) { await roleManager.CreateAsync(new IdentityRole(roleName)); } } }
然后在Startup.cs的Configure方法里调用这个方法(或者在程序启动时注入调用),这样系统启动后就自动有这些角色了。
- 之后控制权限就用
[Authorize(Roles = "Admin")]这种特性,完全和AD组无关,用自己的角色体系就行。
4. 外部登录场景的特殊处理
如果用户是通过外部登录创建的新用户,记得在创建用户后直接分配Member角色,比如在ExternalLoginCallback里:
var info = await _signInManager.GetExternalLoginInfoAsync(); if (info == null) { return RedirectToAction(nameof(Login)); } var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true); if (result.Succeeded) { // 登录成功后检查角色,和之前逻辑一样 var user = await _userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey); var roles = await _userManager.GetRolesAsync(user); if (!roles.Any()) { await _userManager.AddToRoleAsync(user, "Member"); } return RedirectToLocal(returnUrl); } // 如果用户不存在,创建新用户 var newUser = new ApplicationUser { UserName = info.Principal.FindFirstValue(ClaimTypes.Email), Email = info.Principal.FindFirstValue(ClaimTypes.Email) }; var createResult = await _userManager.CreateAsync(newUser); if (createResult.Succeeded) { createResult = await _userManager.AddLoginAsync(newUser, info); if (createResult.Succeeded) { // 直接给新用户加Member角色 await _userManager.AddToRoleAsync(newUser, "Member"); await _signInManager.SignInAsync(newUser, isPersistent: false); return RedirectToLocal(returnUrl); } }
这样整个流程就完全脱离AD组,用自定义角色,并且自动给无角色用户分配Member角色了,都是Identity原生的API,没有额外的复杂依赖,非常简便。
内容的提问来源于stack exchange,提问作者Mohamed Arham

