如何不使用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 { // 接口逻辑... }
三、流程说明
- 获取Token:客户端使用Basic Auth(用户名+密码)调用
api/auth/login接口,验证通过后获取JWT Token。 - 访问受保护接口:后续请求在请求头中携带
Authorization: Bearer {token},即可访问所有标记[Authorize]的接口。 - Token过期处理:当Token过期后,客户端需重新调用
api/auth/login接口,用Basic Auth获取新Token。
内容的提问来源于stack exchange,提问作者SiBrit
相关产品推荐
相关产品推荐

