ABP Framework中移动端如何无需密码生成Access Token或授权码?
ABP 移动端/Blazor WASM 无密码短信验证码认证方案
核心思路
基于ABP集成的IdentityServer扩展自定义授权类型,让移动端、Blazor WASM通过标准OAuth2.0流程向IdentityServer请求Token,而非手动生成。所有Token由IdentityServer统一颁发,确保合法性与安全性。
1. 实现自定义短信验证码授权验证器
ABP的IdentityServer支持扩展授权类型,通过实现IExtensionGrantValidator接口完成短信验证码的验证逻辑:
步骤1:编写验证器
public class SmsCodeGrantValidator : IExtensionGrantValidator { private readonly IIdentityUserRepository _userRepository; private readonly ISmsCodeVerificationService _smsCodeService; // 你已实现的短信验证码验证服务 public string GrantType => "sms_code"; public SmsCodeGrantValidator(IIdentityUserRepository userRepository, ISmsCodeVerificationService smsCodeService) { _userRepository = userRepository; _smsCodeService = smsCodeService; } public async Task ValidateAsync(ExtensionGrantValidationContext context) { var phoneNumber = context.Request.Raw["phone_number"]; var smsCode = context.Request.Raw["sms_code"]; // 验证参数完整性 if (string.IsNullOrEmpty(phoneNumber) || string.IsNullOrEmpty(smsCode)) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "手机号或验证码不能为空"); return; } // 验证短信验证码有效性 var isValid = await _smsCodeService.VerifyCodeAsync(phoneNumber, smsCode); if (!isValid) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "验证码无效或已过期"); return; } // 获取或自动注册用户(根据业务需求调整) var user = await _userRepository.FindByPhoneNumberAsync(phoneNumber); if (user == null) { user = await _userRepository.InsertAsync(new IdentityUser(Guid.NewGuid(), phoneNumber, phoneNumber)); } // 返回验证成功结果 context.Result = new GrantValidationResult( subject: user.Id.ToString(), authenticationMethod: GrantType, claims: new[] { new Claim("phone_number", phoneNumber) } ); } }
步骤2:注册验证器并配置授权类型
在你的ABP模块ConfigureServices方法中添加:
public override void ConfigureServices(ServiceConfigurationContext context) { // 注册自定义授权验证器 context.Services.AddTransient<IExtensionGrantValidator, SmsCodeGrantValidator>(); // 允许自定义授权类型 Configure<AbpIdentityServerOptions>(options => { options.GrantTypes.Add("sms_code"); }); }
2. 移动端调用流程
移动端(iOS/Android)直接与IdentityServer的Token端点交互:
- 发送验证码:用户输入手机号后,APP调用后端短信发送接口(如
/api/account/send-sms-code),后端生成验证码并存储(建议用Redis,设置5分钟过期),同时发送短信。 - 请求Token:用户输入验证码后,向IdentityServer的
/connect/token端点发起POST请求,参数如下:
注:移动端属于公开客户端,无需传grant_type: sms_code client_id: 你的移动端客户端ID(需在ABP IdentityServer客户端配置中创建) phone_number: 用户输入的手机号 sms_code: 用户输入的验证码client_secret,需在客户端配置中设置ClientType为Public。 - 使用Token:验证通过后,IdentityServer返回
access_token、refresh_token等参数,APP存储Token后,后续调用API时在请求头携带Authorization: Bearer {access_token}。
3. Blazor WebAssembly调用流程
Blazor WASM作为前端应用,流程与移动端类似:
- 发送验证码:在登录组件中,用户输入手机号后调用后端短信发送接口。
- 请求Token:用户输入验证码后,通过
HttpClient向/connect/token端点发送POST请求,参数同移动端。 - 更新认证状态:拿到Token后,调用ABP的
AuthManager.LoginAsync方法传入Token,更新应用认证状态,后续API调用会自动携带Token。
示例代码(Blazor WASM):
public async Task LoginWithSmsCode(string phoneNumber, string smsCode) { var tokenResponse = await HttpClient.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string> { ["grant_type"] = "sms_code", ["client_id"] = "YourBlazorClientId", ["phone_number"] = phoneNumber, ["sms_code"] = smsCode })); if (tokenResponse.IsSuccessStatusCode) { var tokenResult = await tokenResponse.Content.ReadFromJsonAsync<TokenResponse>(); await AuthManager.LoginAsync(new AuthTokenProvider(tokenResult.AccessToken, tokenResult.RefreshToken)); NavigationManager.NavigateTo("/"); } else { // 处理错误提示 } }
4. 关键配置与安全注意事项
- 客户端配置:在ABP后台IdentityServer客户端管理中创建对应客户端,需设置:
AllowedGrantTypes包含sms_codeAllowedScopes包含需访问的API Scope(如DefaultApi)- 移动端/WASM客户端设为
Public类型,无需客户端密钥
- 验证码安全:限制短信发送频率(如1分钟内仅允许发送一次),验证码使用6位随机数字,存储时设置5分钟过期
- 用户逻辑:根据业务需求决定是否自动注册手机号用户,或仅允许已注册用户登录
内容的提问来源于stack exchange,提问作者Omer Faruk KAYA
相关产品推荐
相关产品推荐

