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

如何不使用Entity Framework,基于SQL Server实现ASP.NET Core Web API基础认证

解决方案

一、优化UserRepository:避免重复查询数据库

你的问题根源在于UserRepository被注册为作用域(Scoped)服务,导致每次请求都会创建新实例并触发数据库查询。解决思路是将其改为单例(Singleton)服务,并在实例化时一次性加载用户数据到内存,后续请求直接使用内存中的数据。

1. 修改UserRepository实现

public class UserRepository : IUserRepository
{
    private readonly List<User> _users;

    public UserRepository(IConfiguration configuration)
    {
        _users = LoadUsersFromDatabase(configuration.GetConnectionString("YourConnectionString"));
    }

    public User GetUserByUsername(string username)
    {
        return _users.FirstOrDefault(u => u.Username.Equals(username, StringComparison.OrdinalIgnoreCase));
    }

    // 用ADO.NET从SQL Server读取用户数据(无需EF)
    private List<User> LoadUsersFromDatabase(string connectionString)
    {
        var users = new List<User>();
        using var conn = new SqlConnection(connectionString);
        conn.Open();
        using var cmd = new SqlCommand("SELECT Username, PasswordHash, Roles FROM UserList", conn);
        using var reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            users.Add(new User
            {
                Username = reader.GetString(0),
                PasswordHash = reader.GetString(1),
                Roles = reader.GetString(2).Split(',') // 假设Roles是逗号分隔的字符串
            });
        }
        return users;
    }

    // 可选:如果需要手动刷新用户数据,添加此方法
    public void RefreshUsers(IConfiguration configuration)
    {
        var newUsers = LoadUsersFromDatabase(configuration.GetConnectionString("YourConnectionString"));
        lock (_users)
        {
            _users.Clear();
            _users.AddRange(newUsers);
        }
    }
}

2. 在Program.cs中注册为单例

把原来的AddScoped<IUserRepository, UserRepository>()替换为:

builder.Services.AddSingleton<IUserRepository, UserRepository>();

这样UserRepository只会在应用启动时初始化一次,后续所有请求复用同一个实例,直接读取内存中的用户列表,避免重复查库。


二、实现Basic Auth生成Token + Bearer Token认证流程

接下来需要配置双重认证方案:仅登录接口使用Basic Auth验证用户并生成JWT Token,其余接口使用Bearer Token保护。

1. 添加JWT认证依赖

在项目中安装NuGet包:

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

2. 配置认证服务(Program.cs)

builder.Services.AddAuthentication(options =>
{
    // 默认认证方案设为Bearer Token,除非接口指定Basic Auth
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
// 注册Basic认证
.AddBasic<BasicAuthenticationHandler>(options =>
{
    options.Realm = "YourApiRealm";
})
// 注册JWT Bearer认证
.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:SecretKey"]))
    };
});

builder.Services.AddAuthorization();

在appsettings.json中添加JWT配置:

{
  "Jwt": {
    "Issuer": "YourApiIssuer",
    "Audience": "YourApiAudience",
    "SecretKey": "YourStrongSecretKeyAtLeast16CharsLong",
    "ExpirationMinutes": 30
  }
}

3. 编写Login接口(生成Token)

创建一个AuthController,专门处理Token生成,此接口仅允许Basic Auth访问:

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly IUserRepository _userRepository;
    private readonly IConfiguration _configuration;

    public AuthController(IUserRepository userRepository, IConfiguration configuration)
    {
        _userRepository = userRepository;
        _configuration = configuration;
    }

    [HttpPost("login")]
    [Authorize(AuthenticationSchemes = BasicAuthenticationDefaults.AuthenticationScheme)]
    public IActionResult Login()
    {
        // Basic认证通过后,从HttpContext获取当前用户
        var username = User.Identity.Name;
        var user = _userRepository.GetUserByUsername(username);

        if (user == null)
        {
            return Unauthorized();
        }

        // 生成JWT Token
        var token = GenerateJwtToken(user);
        return Ok(new { AccessToken = token });
    }

    private string GenerateJwtToken(User user)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.Username),
            new Claim(ClaimTypes.Role, string.Join(",", user.Roles))
        };

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

        var expiration = DateTime.UtcNow.AddMinutes(Convert.ToDouble(_configuration["Jwt:ExpirationMinutes"]));

        var token = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],
            audience: _configuration["Jwt:Audience"],
            claims: claims,
            expires: expiration,
            signingCredentials: creds);

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

4. 保护其他接口

在需要保护的控制器或Action上添加[Authorize](默认使用Bearer Token):

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class WeatherForecastController : ControllerBase
{
    // 接口逻辑...
}

三、流程说明

  1. 获取Token:客户端使用Basic Auth(用户名+密码)调用api/auth/login接口,验证通过后获取JWT Token。
  2. 访问受保护接口:后续请求在请求头中携带Authorization: Bearer {token},即可访问所有标记[Authorize]的接口。
  3. Token过期处理:当Token过期后,客户端需重新调用api/auth/login接口,用Basic Auth获取新Token。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 09:22:39