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

如何将Web API的认证方式从用户名改为手机号?

解决Web API用手机号+Device ID替代用户名密码认证的问题

我明白你现在的困境——想用手机号和Device ID来做认证,但OAuth的上下文类没法直接修改,只能拿到默认的UserName和Password参数。别担心,我们可以通过几个简单的调整来实现需求:

第一步:从请求参数中获取手机号和Device ID

OAuthGrantResourceOwnerCredentialsContext虽然自带UserName和Password属性,但它的Parameters集合可以获取所有客户端POST过来的参数。你有两种选择:

选项1:复用默认字段(客户端无需改参数名)

让客户端把手机号放在username字段,Device ID放在password字段,直接读取:

var phoneNumber = context.UserName; // 客户端传手机号作为username
var deviceId = context.Password;   // 客户端传Device ID作为password

选项2:使用自定义参数名

如果想更清晰区分字段,直接从Parameters里提取自定义参数:

var phoneNumber = context.Parameters.Get("phoneNumber");
var deviceId = context.Parameters.Get("deviceId");

// 先做参数非空校验
if (string.IsNullOrEmpty(phoneNumber) || string.IsNullOrEmpty(deviceId))
{
    context.SetError("invalid_request", "Phone number and device ID are required.");
    return;
}

第二步:修改认证逻辑,用手机号查找用户并验证Device ID

你不能修改框架自带的OAuthGrantResourceOwnerCredentialsContext,但可以绕过默认的FindAsync方法,自己实现查找和验证逻辑:

1. 通过手机号查找用户

ApplicationUser user = await userManager.Users.FirstOrDefaultAsync(u => u.PhoneNumber == phoneNumber);

if (user == null)
{
    context.SetError("invalid_grant", "The phone number is not registered.");
    return;
}

2. 验证Device ID

分两种场景处理:

  • 如果Device ID作为登录密码:用UserManager的CheckPasswordAsync验证(前提是用户密码哈希是Device ID的哈希值)
if (!await userManager.CheckPasswordAsync(user, deviceId))
{
    context.SetError("invalid_grant", "The device ID is incorrect.");
    return;
}
  • 如果Device ID是用户表单独字段(比如ApplicationUser模型里加了DeviceId属性):直接比对字段值
if (user.DeviceId != deviceId)
{
    context.SetError("invalid_grant", "The device ID does not match.");
    return;
}

3. 保留原有额外校验(可选)

如果还需要验证邮箱是否确认,继续保留你的原有逻辑:

if (!user.EmailConfirmed)
{
    context.SetError("invalid_grant", "User did not confirm email.");
    return;
}

第三步:生成认证票据(和原有逻辑一致)

找到用户并验证通过后,继续生成ClaimsIdentity和认证票据:

ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);

完整修改后的GrantResourceOwnerCredentials方法示例

这里用选项1(复用默认字段)的完整代码:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    var allowedOrigin = "*";
    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });

    var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
    
    // 从默认字段获取手机号和Device ID
    var phoneNumber = context.UserName;
    var deviceId = context.Password;

    // 查找用户
    ApplicationUser user = await userManager.Users.FirstOrDefaultAsync(u => u.PhoneNumber == phoneNumber);
    if (user == null)
    {
        context.SetError("invalid_grant", "The phone number or device ID is incorrect.");
        return;
    }

    // 验证Device ID(这里假设是当作密码)
    if (!await userManager.CheckPasswordAsync(user, deviceId))
    {
        context.SetError("invalid_grant", "The phone number or device ID is incorrect.");
        return;
    }

    // 邮箱确认校验(可选)
    if (!user.EmailConfirmed)
    {
        context.SetError("invalid_grant", "User did not confirm email.");
        return;
    }

    // 生成认证票据
    ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
    var ticket = new AuthenticationTicket(oAuthIdentity, null);
    context.Validated(ticket);
}

额外优化:给UserManager添加自定义查找方法

为了代码复用,你可以在ApplicationUserManager里添加一个通过手机号查找用户的方法:

public async Task<ApplicationUser> FindByPhoneNumberAsync(string phoneNumber)
{
    return await Users.FirstOrDefaultAsync(u => u.PhoneNumber == phoneNumber);
}

之后在认证方法里就可以直接调用await userManager.FindByPhoneNumberAsync(phoneNumber),代码更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:08:01