如何将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
相关产品推荐
相关产品推荐

