feat(system): 实现手机号换绑双重短信验证码验证
- 用户换绑手机号时新增两步短信验证码校验流程 - 第一步:向原手机号(或无原手机号时的新号)发送验证码验证账户归属 - 第二步:向新手机号发送验证码验证新号归属,若账户无旧手机号则跳过 - 新手机号绑定时增加唯一性校验,防止被其他账户占用 - 更新User实体,新增smsCodeNew属性用于存储新手机号验证码 - 优化Redis验证码缓存和删除逻辑,防止验证码滥用
This commit is contained in:
@@ -392,21 +392,43 @@ public class MainController extends BaseController {
|
||||
update.setAvatar(user.getAvatar());
|
||||
update.setBgImage(user.getBgImage());
|
||||
update.setSex(user.getSex());
|
||||
// 手机号变更:仅当提交短信验证码且校验通过时才更新,防止未经验证的手机号被绑定
|
||||
// 手机号变更:两步验证
|
||||
// 第一步:短信验证码发往原手机号(验证当前账户归属),校验通过才允许换绑
|
||||
// 第二步:向新手机号发送验证码(验证新号归属);账号原本无手机号时,第一步的码已发往新号,无需第二步
|
||||
if (StrUtil.isNotBlank(user.getSmsCode())) {
|
||||
String newPhone = user.getPhone();
|
||||
if (StrUtil.isBlank(newPhone) || !CommonUtil.isValidPhoneNumber(newPhone)) {
|
||||
return fail("手机号格式不正确", null);
|
||||
}
|
||||
String key = "code:" + newPhone;
|
||||
String cached = redisUtil.get(key);
|
||||
User currentUser = userService.getById(getLoginUserId());
|
||||
String currentPhone = currentUser != null ? currentUser.getPhone() : null;
|
||||
// 第一步:校验原手机号(或账号原本无手机号时的新号)验证码
|
||||
String verifyPhone = StrUtil.isNotBlank(currentPhone) ? currentPhone : newPhone;
|
||||
String oldKey = "code:" + verifyPhone;
|
||||
String cachedOld = redisUtil.get(oldKey);
|
||||
String devCode = redisUtil.get(CACHE_KEY_VERIFICATION_CODE_BY_DEV_SMS);
|
||||
if (StrUtil.isBlank(cached) || (!cached.equals(user.getSmsCode()) && !user.getSmsCode().equals(devCode))) {
|
||||
if (StrUtil.isBlank(cachedOld) || (!cachedOld.equals(user.getSmsCode()) && !user.getSmsCode().equals(devCode))) {
|
||||
return fail("短信验证码不正确", null);
|
||||
}
|
||||
// 第二步:校验新手机号验证码(仅当账号原本已绑定手机号,才需要二次验证新号归属)
|
||||
if (StrUtil.isNotBlank(currentPhone)) {
|
||||
String newKey = "code:" + newPhone;
|
||||
String cachedNew = redisUtil.get(newKey);
|
||||
if (StrUtil.isBlank(cachedNew)
|
||||
|| (!cachedNew.equals(user.getSmsCodeNew()) && !user.getSmsCodeNew().equals(devCode))) {
|
||||
return fail("新手机号验证码不正确", null);
|
||||
}
|
||||
redisUtil.delete(newKey);
|
||||
cacheClient.delete(newPhone);
|
||||
}
|
||||
// 新手机号唯一性校验:不可被其他账号占用
|
||||
User existed = userService.getByPhone(newPhone);
|
||||
if (existed != null && !existed.getUserId().equals(getLoginUserId())) {
|
||||
return fail("该手机号已被其他账号绑定", null);
|
||||
}
|
||||
update.setPhone(newPhone);
|
||||
redisUtil.delete(key);
|
||||
cacheClient.delete(newPhone);
|
||||
redisUtil.delete(oldKey);
|
||||
cacheClient.delete(verifyPhone);
|
||||
}
|
||||
// 邮箱变更:仅当提交邮箱验证码且校验通过时才更新,防止未经验证的邮箱被绑定
|
||||
if (StrUtil.isNotBlank(user.getEmailCode())) {
|
||||
|
||||
@@ -69,6 +69,10 @@ public class User implements UserDetails {
|
||||
@TableField(exist = false)
|
||||
private String smsCode;
|
||||
|
||||
@Schema(description = "新手机号短信验证码(非数据库字段,仅用于换绑手机号时二次验证新号归属)")
|
||||
@TableField(exist = false)
|
||||
private String smsCodeNew;
|
||||
|
||||
@Schema(description = "资质")
|
||||
private String aptitude;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user