23、手机验证码登录
23.1、需求分析
1、为了方便用户登录,移动端通常都会提供通过手机验证码登录的功能。
2、手机验证码登录的优点:
方便快捷,无需注册,直接登录使用短信验证码作为登录凭证,无需记忆密码安全
3、登录流程:
输入手机号码>获取验证码>输入验证码>点击登录>登录成功
注意:通过手机验证码登录,手机号是区分不同用户的标识。
23.2、数据模型
通过手机验证码登录时,涉及的表为user
表,即用户表。结构如下:
23.3、代码开发
23.3.1、梳理交互过程
在开发代码之前,需要梳理一下登录时前端页面和服务端的交互过程:
1、在登录页面(front/page/login.html
)输入手机号,点击【获取验证码】按钮,页面发送ajax
请求,在服务端调用短信服务API
给指定的手机号发送验证码短信。
2、在登录页面输入验证码,点击【登录】按钮,发送ajax
请求,在服务端处理登录请求
开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
23.3.2、准备工作
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
实体类User
/*** 用户信息*/@Datapublic class User implements Serializable {private static final long serialVersionUID = 1L;private Long id;//姓名private String name;//手机号private String phone;//性别 0 女 1 男private String sex;//身份证号private String idNumber;//头像private String avatar;//状态 0:禁用,1:正常private Integer status;}
Mapper
接口UserMapper
@Mapperpublic interface UserMapper extends BaseMapper<User> {}
业务层接口Userservice
public interface UserService extends IService<User> {}
业务层实现类UserServiceImpl
@Servicepublic class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}
控制层UserController
@RestController@RequestMapping("/user")@Slf4jpublic class UserController {@Autowiredprivate UserService userService;}
工具类SMSUtils
、validateCodeUtils
【SMSUtils.java
】
/*** 短信发送工具类*/public class SMSUtils {/*** 发送短信* @param signName 签名* @param templateCode 模板* @param phoneNumbers 手机号* @param param 参数*/public static void sendMessage(String signName, String templateCode,String phoneNumbers,String param){DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "", "");IAcsClient client = new DefaultAcsClient(profile);SendSmsRequest request = new SendSmsRequest();request.setSysRegionId("cn-hangzhou");request.setPhoneNumbers(phoneNumbers);request.setSignName(signName);request.setTemplateCode(templateCode);request.setTemplateParam("{\"code\":\""+param+"\"}");try {SendSmsResponse response = client.getAcsResponse(request);System.out.println("短信发送成功");}catch (ClientException e) {e.printStackTrace();}}}
【validateCodeUtils.java
】
/*** 随机生成验证码工具类*/public class ValidateCodeUtils {/*** 随机生成验证码* @param length 长度为4位或者6位* @return*/public static Integer generateValidateCode(int length){Integer code =null;if(length == 4){code = new Random().nextInt(9999);//生成随机数,最大为9999if(code < 1000){code = code + 1000;//保证随机数为4位数字}}else if(length == 6){code = new Random().nextInt(999999);//生成随机数,最大为999999if(code < 100000){code = code + 100000;//保证随机数为6位数字}}else{throw new RuntimeException("只能生成4位或6位数字验证码");}return code;}/*** 随机生成指定长度字符串验证码* @param length 长度* @return*/public static String generateValidateCode4String(int length){Random rdm = new Random();String hash1 = Integer.toHexString(rdm.nextInt());String capstr = hash1.substring(0, length);return capstr;}}
23.3.3、修改LoginCheckFilter
1、前面我们已经完成了LoginCheckFilter
过滤器的开发,此过滤器用于检查用户的登录状态。我们在进行手机验证码登录时,发送的请求需要在此过滤器处理时直接放行。
//定义不需要处理的请求路径String[] urls = new String[]{"/employee/login","/employee/logout","/backend/**","/front/**","/common/**","/user/sendMsg",//移动端发送短信"/user/login" //移动端登录};
2、在LoginCheckFilter
过滤器中扩展逻辑,判断移动端用户登录状态:
//4-2、判断登录状态,如果已登录,则直接放行if (request.getSession().getAttribute("user") != null) {log.info("用户已登录,用户id为:{}", request.getSession().getAttribute("user"));Long userId = (Long) request.getSession().getAttribute("user");BaseContext.setCurrentId(userId);filterChain.doFilter(request, response);return;}
23.4、功能测试
23.4.1、发送手机验证码
1、前端开发
2、Controller
开发
/*** 发送手机验证码* @param user* @return*/@PostMapping("/sendMsg")public R<String> sendMsg(@RequestBody User user, HttpSession session) {//获取手机号String phone = user.getPhone();if (StringUtils.isNotEmpty(phone)) {//生成随机的4位验证码String code = ValidateCodeUtils.generateValidateCode(4).toString();log.info("code={}", code);//调用阿里云提供的短信服务API完成发送短信//SMSUtils.sendMessage("瑞吉外卖", "", phone, code);//需要将生成的验证码保存到Sessionsession.setAttribute(phone, code);return R.success("手机验证码短信发送成功");}return R.error("手机验证码短信发送失败");}
23.4.2、移动端登录
1、前端开发
2、Controller
开发
/*** 移动端用户登录* @param map* @param session* @return*/@PostMapping("/login")public R<User> login(@RequestBody Map map, HttpSession session) {log.info(map.toString());//获取手机号String phone = map.get("phone").toString();//获取验证码String code = map.get("code").toString();//从Session获取保存的验证码String codeInSession = session.getAttribute(phone).toString();//进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)if (codeInSession != null && codeInSession.equals(code)) {//如果比对成功,说明登录成功LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getPhone, phone);User user = userService.getOne(queryWrapper);if (user == null) {//判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册user = new User();user.setPhone(phone);user.setStatus(1);userService.save(user);}session.setAttribute("user",user);return R.success(user);}return R.error("登录失败");}