Springboot 手机号短信验证码注册登录实现

2026-08-20 19:09:00

本文将介绍如何在Spring Boot应用中实现短信验证码注册和登录功能,为用户提供便捷的移动端认证体验。

1. 功能概述我们将实现以下功能:

手机号+验证码注册手机号+验证码登录2. 技术栈Spring Boot 3.2.0互亿无线短信平台MyBatis FlexJWT3. 实现步骤3.1 短信服务集成首先,需要在application.properties中配置短信平台信息:

# 短信配置

sms.account=C09251523

sms.apikey=826efdf7322e3c49355428a523c10eec3.2 创建短信工具类创建SmsUtil工具类处理短信发送:

@Component

public class SmsUtil {

private static final Logger logger = LoggerFactory.getLogger(SmsUtil.class);

// 短信发送API地址

private static final String SMS_API_URL = "http://106.ihuyi.com/webservice/sms.php?method=Submit";

// API账号和密码

@Value("${sms.account:C09***523}")

private String account;

@Value("${sms.apikey:826efdf7322e3c49355***a523c10eec}")

private String apiKey;

/**

* 发送短信验证码

* @param phone 手机号

* @param code 验证码

* @return 发送结果,包含code和msg

*/

public Map sendVerificationCode(String phone, String code) {

Map result = new HashMap<>();

try {

// 短信内容

String content = "您的验证码是:" + code + "。请不要把验证码泄露给其他人。";

// 发送请求

String response = sendSmsRequest(phone, content);

// 解析结果

result = parseXmlResponse(response);

// 记录日志

if ("2".equals(result.get("code"))) {

logger.info("短信发送成功,手机号: {}, 验证码: {}", phone, code);

} else {

logger.error("短信发送失败,手机号: {}, 错误信息: {}", phone, result.get("msg"));

}

} catch (Exception e) {

logger.error("发送短信验证码异常", e);

result.put("code", "0");

result.put("msg", "系统异常,短信发送失败");

}

return result;

}

// 省略发送请求和解析响应的方法...

}3.3 验证码生成和存储在UserServiceImpl中实现验证码管理:

// 在UserServiceImpl中

private final Map smsCodeCache = new HashMap<>();

@Override

public boolean sendSmsCode(String phone, String type) {

try {

// 校验手机号格式

if (!isValidPhoneNumber(phone)) {

return false;

}

// 生成6位随机验证码

String code = generateRandomCode(6);

// 将验证码存入缓存,实际项目中应使用Redis等缓存服务

smsCodeCache.put(phone + ":" + type, code);

// 发送短信

Map result = smsUtil.sendVerificationCode(phone, code);

// 检查发送结果

return "2".equals(result.get("code"));

} catch (Exception e) {

logger.error("发送短信验证码失败", e);

return false;

}

}

@Override

public String getSmsCode(String phone, String type) {

return smsCodeCache.get(phone + ":" + type);

}

@Override

public void removeSmsCode(String phone, String type) {

smsCodeCache.remove(phone + ":" + type);

}3.4 控制器实现发送短信验证码@PostMapping("/send/sms")

public ResponseEntity sendSmsCode(

@RequestParam @NotBlank @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") String phone,

@RequestParam @NotBlank String type) {

logger.info("发送短信验证码,手机号: {}, 类型: {}", phone, type);

boolean result = userService.sendSmsCode(phone, type);

if (result) {

return ResponseEntity.ok().body(Map.of("code", 200, "message", "验证码发送成功"));

} else {

return ResponseEntity.badRequest().body(Map.of("code", 400, "message", "验证码发送失败"));

}

}手机号+验证码注册@PostMapping("/register/sms")

public ResponseEntity registerBySms(

@RequestBody(required = false) Map registerRequest,

@RequestParam(required = false) String phone,

@RequestParam(required = false) String password,

@RequestParam(required = false) String code,

@RequestParam(required = false) String nickname) {

// 优先使用URL参数,如果没有则使用请求体

if (registerRequest != null) {

if (phone == null) phone = registerRequest.get("phone");

if (password == null) password = registerRequest.get("password");

if (code == null) code = registerRequest.get("code");

if (nickname == null) nickname = registerRequest.get("nickname");

}

if (phone == null || password == null || code == null) {

return ResponseEntity.badRequest().body(Map.of(

"code", 400,

"message", "手机号、密码和验证码不能为空"));

}

// 验证手机号格式

if (!phone.matches("^1[3-9]\\d{9}$")) {

return ResponseEntity.badRequest().body(Map.of(

"code", 400,

"message", "手机号格式不正确"));

}

logger.info("短信验证码一步式注册,手机号: {}", phone);

// 验证验证码

String cachedCode = userService.getSmsCode(phone, "register");

if (cachedCode == null || !cachedCode.equals(code)) {

return ResponseEntity.status(400).body(Map.of(

"code", 400,

"message", "验证码错误或已过期"));

}

// 创建用户

User user = new User();

user.setPhone(phone);

user.setUsername(phone); // 使用手机号作为用户名

user.setPassword(password); // 未加密的密码,在service层会加密

user.setNickname(nickname != null ? nickname : "用户" + phone.substring(phone.length() - 4));

// 注册用户

Map registerResult = userService.register(user, true);

int registerCode = (int) registerResult.get("code");

// 验证码验证成功后,删除缓存中的验证码

if (registerCode == 200) {

userService.removeSmsCode(phone, "register");

}

return ResponseEntity.status(registerCode == 200 ? 200 : 400).body(registerResult);

}短信验证码登录@PostMapping("/login/sms")

public ResponseEntity loginBySms(

@RequestParam @NotBlank @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") String phone,

@RequestParam @NotBlank String code) {

logger.info("短信验证码登录,手机号: {}", phone);

Map result = userService.smsLogin(phone, code);

int code2 = (int) result.get("code");

return ResponseEntity.status(code2 == 200 ? 200 : 400).body(result);

}3.5 服务层实现实现短信验证码登录的业务逻辑:

@Override

public Map smsLogin(String phone, String code) {

Map result = new HashMap<>();

// 验证验证码

String cachedCode = getSmsCode(phone, "login");

if (cachedCode == null || !cachedCode.equals(code)) {

result.put("code", 400);

result.put("message", "验证码错误或已过期");

return result;

}

// 清除验证码缓存

removeSmsCode(phone, "login");

// 根据手机号查询用户

User user = getOneOrNull(QueryWrapper.create().where("phone = ?", phone));

if (user == null) {

result.put("code", 400);

result.put("message", "用户不存在,请先注册");

return result;

}

// 检查用户状态

if (user.getStatus() != 1) {

result.put("code", 403);

result.put("message", "账号已被禁用");

return result;

}

// 更新登录信息

user.setLastLoginTime(LocalDateTime.now());

updateById(user);

// 生成token

String token = jwtUtil.generateToken(user.getUsername());

// 准备返回数据

Map userData = new HashMap<>();

userData.put("id", user.getId());

userData.put("username", user.getUsername());

userData.put("nickname", user.getNickname());

userData.put("avatar", user.getAvatar());

userData.put("phone", user.getPhone());

result.put("code", 200);

result.put("message", "登录成功");

result.put("data", new HashMap() {{

put("token", token);

put("user", userData);

}});

return result;

}4. 前端实现4.1 API层// 用户相关接口

export const userApi = {

// 手机号验证码注册

registerBySms: (data) => {

return request({

url: '/user/register/sms',

method: 'post',

data

})

},

// 手机号验证码登录

smsLogin: (data) => {

return request({

url: '/user/login/sms',

method: 'post',

data

})

},

// 发送短信验证码

sendSmsCode: (phone, type) => {

return request({

url: '/user/send/sms',

method: 'post',

params: {

phone,

type // login-登录, register-注册, reset-重置密码

}

})

}

}4.2 手机号注册页面

5. 最佳实践验证码安全

设置验证码过期时间(通常5分钟)限制发送频率,避免短信轰炸使用Redis存储验证码,设置TTL手机号验证

正则验证手机号格式检查手机号是否已注册短信内容

遵守短信平台规范内容简洁明了包含必要的安全提示防护措施

IP限制图形验证码预校验短信发送频率限制6. 总结短信验证码注册登录是移动互联网应用的标准功能,不仅提高了用户体验,也增强了账户安全性。本文介绍了实现这一功能的完整流程,从后端API到前端UI。

在实际应用中,可以根据需求进一步优化:

添加图形验证码预校验使用Redis存储验证码实现短信模板多样化增加短信登录的安全策略