如何在C# ASP.NET Core 8 Web API(Identity User)中用手机号替代邮箱注册登录
基于ASP.NET Core 8.0 Identity的手机号登录/注册实现方案
以下是完整的替代邮箱、用手机号完成注册登录的落地方案,涵盖核心代码与配置细节:
1. 自定义IdentityUser实体
默认IdentityUser以邮箱为核心标识,我们需要自定义实体,将手机号设为必填且唯一的核心字段:
using Microsoft.AspNetCore.Identity; using System.ComponentModel.DataAnnotations; public class AppUser : IdentityUser { // 移除邮箱必填约束(若不需要邮箱字段可直接删除) public override string? Email { get; set; } public override bool EmailConfirmed { get; set; } // 强制手机号必填、限定格式 [Required(ErrorMessage = "手机号不能为空")] [Phone(ErrorMessage = "手机号格式不正确")] [MaxLength(20)] public override string PhoneNumber { get; set; } = string.Empty; }
2. 更新DbContext
让Identity使用自定义的AppUser实体:
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; public class AppDbContext : IdentityDbContext<AppUser> { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } }
3. 配置Identity服务(Program.cs)
调整Identity的默认规则,适配手机号作为核心标识:
builder.Services.AddIdentity<AppUser, IdentityRole>(options => { // 关闭邮箱唯一性要求 options.User.RequireUniqueEmail = false; // 允许用户名包含手机号的合法字符(如数字、+、-) options.User.AllowedUserNameCharacters = "0123456789+-"; // 可选:强制登录前需验证手机号 options.SignIn.RequireConfirmedPhoneNumber = true; // 按需配置密码复杂度规则 options.Password.RequireDigit = true; options.Password.RequireLowercase = true; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequiredLength = 6; }) .AddEntityFrameworkStores<AppDbContext>() .AddDefaultTokenProviders(); // 用于生成手机号验证令牌
4. 实现注册接口
接收手机号与密码,完成用户创建与可选的手机号验证:
[ApiController] [Route("api/auth")] public class AuthController : ControllerBase { private readonly UserManager<AppUser> _userManager; public AuthController(UserManager<AppUser> userManager) { _userManager = userManager; } [HttpPost("register")] public async Task<IActionResult> Register([FromBody] RegisterRequest request) { if (!ModelState.IsValid) return BadRequest(ModelState); // 检查手机号是否已注册 var existingUser = await _userManager.FindByPhoneNumberAsync(request.PhoneNumber); if (existingUser != null) return BadRequest("该手机号已被注册"); // 创建用户,将手机号设为用户名(适配Identity默认登录逻辑) var user = new AppUser { UserName = request.PhoneNumber, PhoneNumber = request.PhoneNumber }; var createResult = await _userManager.CreateAsync(user, request.Password); if (!createResult.Succeeded) return BadRequest(createResult.Errors.Select(e => e.Description)); // 生成手机号验证令牌(需自行集成短信服务发送) var verifyToken = await _userManager.GenerateChangePhoneNumberTokenAsync(user, request.PhoneNumber); // TODO: 调用第三方短信API发送verifyToken给用户 return Ok("注册成功,请查收短信验证码完成验证"); } // 注册请求DTO public class RegisterRequest { [Required] [Phone] public string PhoneNumber { get; set; } = string.Empty; [Required] [DataType(DataType.Password)] public string Password { get; set; } = string.Empty; } }
5. 实现手机号登录接口
直接通过手机号查找用户并验证密码:
[HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginRequest request) { if (!ModelState.IsValid) return BadRequest(ModelState); // 通过手机号查找用户 var user = await _userManager.FindByPhoneNumberAsync(request.PhoneNumber); if (user == null) return Unauthorized("手机号或密码错误"); // 验证密码(若开启了手机号验证,需先检查PhoneNumberConfirmed) if (!user.PhoneNumberConfirmed) return Unauthorized("请先验证手机号"); var passwordResult = await _userManager.CheckPasswordAsync(user, request.Password); if (!passwordResult) return Unauthorized("手机号或密码错误"); // TODO: 生成JWT令牌(需自行实现JWT配置与生成逻辑) var jwtToken = GenerateJwtToken(user); return Ok(new { Token = jwtToken }); } // 登录请求DTO public class LoginRequest { [Required] [Phone] public string PhoneNumber { get; set; } = string.Empty; [Required] [DataType(DataType.Password)] public string Password { get; set; } = string.Empty; }
6. 补充:手机号验证接口
用于用户提交短信验证码完成手机号验证:
[HttpPost("verify-phone")] public async Task<IActionResult> VerifyPhone([FromBody] VerifyPhoneRequest request) { if (!ModelState.IsValid) return BadRequest(ModelState); var user = await _userManager.FindByPhoneNumberAsync(request.PhoneNumber); if (user == null) return BadRequest("用户不存在"); var verifyResult = await _userManager.VerifyChangePhoneNumberTokenAsync(user, request.VerifyToken, request.PhoneNumber); if (!verifyResult) return BadRequest("验证码无效或已过期"); // 标记手机号已验证 user.PhoneNumberConfirmed = true; await _userManager.UpdateAsync(user); return Ok("手机号验证成功"); } public class VerifyPhoneRequest { [Required] [Phone] public string PhoneNumber { get; set; } = string.Empty; [Required] public string VerifyToken { get; set; } = string.Empty; }
关键注意事项
- 数据库迁移:修改实体后需执行
Add-Migration PhoneNumberAsPrimary和Update-Database更新数据库结构 - 短信服务集成:需自行对接第三方短信平台(如阿里云、腾讯云)实现验证码发送
- JWT配置:若使用JWT认证,需在Program.cs中添加
AddJwtBearer配置,并实现GenerateJwtToken方法生成令牌
内容的提问来源于stack exchange,提问作者MAD_CRAZY
相关产品推荐
相关产品推荐

