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

.NET 8无脚手架+自定义数据库实现自定义角色提供者方案

ASP.NET Core 8 MVC 自定义角色校验实现方案

针对你的自定义表结构(User/Role/UserRole),无需Code First或脚手架,以下是兼容[Authorize(Roles)]和IsInRole()的实现步骤:

1. 定义实体类

对应自定义数据库表,仅用于数据映射:

// User表实体
public class CustomUser
{
    public Guid UserId { get; set; }
    public string Email { get; set; }
}

// Role表实体
public class CustomRole
{
    public Guid RoleId { get; set; }
    public string Title { get; set; }
}

// UserRole关联表实体
public class CustomUserRole
{
    public Guid UserId { get; set; }
    public Guid RoleId { get; set; }
}

2. 实现角色数据访问类

负责从数据库查询用户关联的角色:

public class CustomRoleRepository
{
    private readonly string _connectionString;

    public CustomRoleRepository(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("YourDbConnection");
    }

    // 根据用户ID获取所有角色名称
    public async Task<List<string>> GetUserRolesAsync(Guid userId)
    {
        var roles = new List<string>();
        using (var conn = new SqlConnection(_connectionString))
        {
            await conn.OpenAsync();
            var sql = @"
                SELECT r.Title 
                FROM UserRole ur
                JOIN Role r ON ur.RoleId = r.RoleId
                WHERE ur.UserId = @UserId";
            var cmd = new SqlCommand(sql, conn);
            cmd.Parameters.AddWithValue("@UserId", userId);
            using (var reader = await cmd.ExecuteReaderAsync())
            {
                while (await reader.ReadAsync())
                {
                    roles.Add(reader.GetString(0));
                }
            }
        }
        return roles;
    }
}

3. 两种角色集成方案

方案一:登录时一次性加载角色(适合角色变动频率低的场景)

在登录逻辑中验证用户后,将角色作为Claim添加到认证主体:

public async Task<IActionResult> Login(LoginModel model)
{
    // 1. 自定义用户验证逻辑(查询User表校验邮箱和密码)
    var user = await ValidateUserAsync(model.Email, model.Password);
    if (user == null)
    {
        ModelState.AddModelError("", "账号或密码错误");
        return View(model);
    }

    // 2. 获取用户角色
    var roleRepo = HttpContext.RequestServices.GetRequiredService<CustomRoleRepository>();
    var roles = await roleRepo.GetUserRolesAsync(user.UserId);

    // 3. 构建认证Claims
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString()),
        new Claim(ClaimTypes.Email, user.Email)
    };
    // 添加角色声明
    foreach (var role in roles)
    {
        claims.Add(new Claim(ClaimTypes.Role, role));
    }

    var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    var principal = new ClaimsPrincipal(identity);

    // 4. 执行登录
    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties
    {
        IsPersistent = model.RememberMe,
        ExpiresUtc = DateTimeOffset.UtcNow.AddDays(7)
    });

    return RedirectToAction("Index", "Home");
}

// 自行实现用户验证方法
private async Task<CustomUser> ValidateUserAsync(string email, string password)
{
    // 查询User表,验证邮箱和密码(注意密码应存储哈希值,此处仅为示例)
    using (var conn = new SqlConnection(_connectionString))
    {
        await conn.OpenAsync();
        var sql = "SELECT UserId, Email FROM User WHERE Email = @Email AND Password = @Password";
        var cmd = new SqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("@Email", email);
        cmd.Parameters.AddWithValue("@Password", password); // 实际应使用哈希校验
        using (var reader = await cmd.ExecuteReaderAsync())
        {
            if (await reader.ReadAsync())
            {
                return new CustomUser
                {
                    UserId = reader.GetGuid(0),
                    Email = reader.GetString(1)
                };
            }
        }
    }
    return null;
}

方案二:动态加载角色(适合角色实时变动,无需重新登录的场景)

实现IClaimsTransformation接口,每次请求时自动同步最新角色:

public class CustomClaimsTransformer : IClaimsTransformation
{
    private readonly CustomRoleRepository _roleRepo;
    private readonly IMemoryCache _cache;

    public CustomClaimsTransformer(CustomRoleRepository roleRepo, IMemoryCache cache)
    {
        _roleRepo = roleRepo;
        _cache = cache;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        if (!principal.Identity.IsAuthenticated)
            return principal;

        if (!Guid.TryParse(principal.FindFirstValue(ClaimTypes.NameIdentifier), out var userId))
            return principal;

        // 使用缓存减少数据库查询压力(缓存10分钟)
        var cacheKey = $"UserRoles_{userId}";
        var latestRoles = await _cache.GetOrCreateAsync(cacheKey, async entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
            return await _roleRepo.GetUserRolesAsync(userId);
        });

        var identity = principal.Identity as ClaimsIdentity;
        var existingRoles = principal.FindAll(ClaimTypes.Role).Select(c => c.Value).ToList();

        // 添加新增角色
        foreach (var role in latestRoles)
        {
            if (!existingRoles.Contains(role))
                identity.AddClaim(new Claim(ClaimTypes.Role, role));
        }

        // 移除已取消的角色
        foreach (var existingRole in existingRoles)
        {
            if (!latestRoles.Contains(existingRole))
            {
                var claimToRemove = identity.FindFirst(c => c.Type == ClaimTypes.Role && c.Value == existingRole);
                if (claimToRemove != null)
                    identity.RemoveClaim(claimToRemove);
            }
        }

        return principal;
    }
}

4. 配置服务与中间件

在Program.cs中注册服务并启用认证授权:

var builder = WebApplication.CreateBuilder(args);

// 添加控制器和视图服务
builder.Services.AddControllersWithViews();

// 注册角色数据访问类和Claims转换器(方案二需要)
builder.Services.AddScoped<CustomRoleRepository>();
builder.Services.AddScoped<IClaimsTransformation, CustomClaimsTransformer>();

// 配置Cookie认证
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Account/Login";
        options.AccessDeniedPath = "/Account/AccessDenied";
    });

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

var app = builder.Build();

// 中间件配置
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

// 必须先启用认证,再启用授权
app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

5. 使用方式

控制器/Action授权

直接使用[Authorize(Roles)]特性,支持多角色逗号分隔:

[Authorize(Roles = "Admin,Editor")]
public IActionResult ManageContent()
{
    return View();
}

Razor视图角色校验

使用User.IsInRole()方法:

@if (User.IsInRole("Admin"))
{
    <div class="admin-menu">
        <a href="/Admin/Dashboard">管理员面板</a>
    </div>
}

注意事项

  • 确保appsettings.json中配置正确的数据库连接字符串:
{
  "ConnectionStrings": {
    "YourDbConnection": "Server=.;Database=你的数据库名;Trusted_Connection=True;TrustServerCertificate=True;"
  }
}
  • 密码存储必须使用哈希算法(如BCrypt),禁止明文存储
  • 方案二的缓存时间可根据业务需求调整,平衡实时性与性能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 13:47:04