如何为基于Identity的MVC应用添加JWT认证以支持原生移动应用
在ASP.NET Core Identity MVC应用中添加JWT认证支持原生客户端
核心思路
无需引入第三方中间件,直接基于现有Identity体系扩展JWT认证能力,同时保留原MVC的Cookie认证,实现双认证模式共存,不用大幅重写现有代码。
步骤1:配置JWT认证服务
安装必要NuGet包:
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer Install-Package System.IdentityModel.Tokens.Jwt在
Program.cs中添加JWT认证配置(保留原Cookie认证):var builder = WebApplication.CreateBuilder(args); // 保留原有Identity配置 builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); // 读取JWT配置参数 var jwtSettings = builder.Configuration.GetSection("JwtSettings"); var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"])); // 配置双认证模式:Cookie用于MVC网页,JWT用于原生客户端 builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie() .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtSettings["Issuer"], ValidAudience = jwtSettings["Audience"], IssuerSigningKey = securityKey }; }); // 可选:创建授权策略,允许Cookie和JWT两种方式访问API builder.Services.AddAuthorization(options => { options.AddPolicy("AllowDualAuth", policy => { policy.AuthenticationSchemes.Add(CookieAuthenticationDefaults.AuthenticationScheme); policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); policy.RequireAuthenticatedUser(); }); });在
appsettings.json中添加JWT配置项:"JwtSettings": { "SecretKey": "your-strong-secret-key-at-least-16-characters", "Issuer": "your-app-domain", "Audience": "native-client-audience", "ExpirationMinutes": 60 }
步骤2:添加登录端点生成JWT
创建API控制器处理原生客户端登录请求,复用Identity的用户验证逻辑:
[ApiController] [Route("api/auth")] public class AuthController : ControllerBase { private readonly UserManager<IdentityUser> _userManager; private readonly IConfiguration _configuration; public AuthController(UserManager<IdentityUser> userManager, IConfiguration configuration) { _userManager = userManager; _configuration = configuration; } [HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginRequest request) { // 用Identity验证用户名密码 var user = await _userManager.FindByNameAsync(request.Username); if (user == null || !await _userManager.CheckPasswordAsync(user, request.Password)) { return Unauthorized("用户名或密码错误"); } // 组装JWT声明信息 var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim(ClaimTypes.Name, user.UserName) }; // 可选:添加用户角色声明 var roles = await _userManager.GetRolesAsync(user); claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); // 生成JWT令牌 var jwtSettings = _configuration.GetSection("JwtSettings"); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"])); var signingCreds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: jwtSettings["Issuer"], audience: jwtSettings["Audience"], claims: claims, expires: DateTime.Now.AddMinutes(Convert.ToDouble(jwtSettings["ExpirationMinutes"])), signingCredentials: signingCreds); return Ok(new { Token = new JwtSecurityTokenHandler().WriteToken(token), Expiration = token.ValidTo }); } } // 登录请求数据模型 public class LoginRequest { public string Username { get; set; } public string Password { get; set; } }
步骤3:保护API端点
根据需求选择认证方式保护接口:
仅允许原生客户端用JWT访问:
[ApiController] [Route("api/protected")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] public class ProtectedDataController : ControllerBase { // 接口逻辑 }同时允许MVC网页(Cookie)和原生客户端(JWT)访问:
[ApiController] [Route("api/shared")] [Authorize(Policy = "AllowDualAuth")] public class SharedDataController : ControllerBase { // 接口逻辑 }
原生客户端使用流程
- 向
POST /api/auth/login发送JSON请求,携带用户名密码:{ "Username": "example-user", "Password": "example-pass123" } - 验证通过后,获取返回的
Token和Expiration字段; - 后续请求在HTTP头中添加
Authorization: Bearer {token},即可访问受保护的API端点。
内容的提问来源于stack exchange,提问作者Donald Hughes
相关产品推荐
相关产品推荐

