赞
踩
为了响应公司项目的特定需求,增强用户体验与安全性,集成手机短信验证码登录功能至基于若依微服务框架开发的应用中,故创作此篇为未来类似项目提供了可借鉴的实施范例。
大致可分为这几个阶段:生成验证码并存储至redis缓存中,发送验证码,用户登陆验证
/** * 发送手机验证码 * @param phoneNumber * @return */ @GetMapping("/code/{phoneNumber}") public AjaxResult sendCode(@PathVariable String phoneNumber) { // 从Redis中获取最后一次发送验证码的时间戳 String lastSentTimeStr = redisService.getCacheObject(phoneNumber + "lastSendTime"); if (lastSentTimeStr != null) { long lastSentTime = Long.parseLong(lastSentTimeStr); // 计算当前时间与上次发送时间的时间差 long timeDiff = Duration.between(Instant.ofEpochMilli(lastSentTime), Instant.now()).toMillis(); // 如果时间差小于60秒,则返回错误 if (timeDiff < 60_000) { return AjaxResult.error("您的短信发送过于频繁,请60s后再试!"); } } //生成6位随机验证码 Random randObj = new Random(); String smsCode = Integer.toString(100000 + randObj.nextInt(900000)); //发送短信 boolean isSend = sysSmsApiService.send(phoneNumber,smsCode); if(!isSend){ return AjaxResult.error("短信发送失败!"); } // 更新Redis中该手机号最后发送验证码的时间戳 redisService.setCacheObject(phoneNumber + "lastSendTime", String.valueOf(Instant.now().toEpochMilli()), 60L, TimeUnit.SECONDS); //将验证码保存至redis缓存中,设置有限期为5分钟 redisService.setCacheObject(phoneNumber,smsCode,5L,TimeUnit.MINUTES); return AjaxResult.success(); }
@Value("${tax.url}") private String url; @Value("${tax.key}") private String key; @Value("${tax.vipara}") private String vipara; /** * 发送短信 * @param phoneNumber * @param smsCode * @return */ @Override public boolean send(String phoneNumber, String smsCode) { try { SendtoVo sendtoVo = new SendtoVo(); sendtoVo.setSjhm(phoneNumber); String content = "短信模板"; //自己根据业务需求编写 sendtoVo.setContent(content); HashMap<Object, Object> map = new HashMap<>(); map.put("*******", "********"); map.put("data", JSON.toJSONString(sendtoVo)); Map result = doPostUtils.doPost(url, map, key, vipara); //此处传入小程序配置参数 // System.out.println("短信发送返回结果======================:" + result); if (result != null) { return true; } else { return false; } } catch (Exception e) { e.printStackTrace(); } return false; }
/** * 向指定 URL 发送POST方法的请求 * * @param httpUrl * 发送请求的 URL * @param map * 请求参数是json * @param key * key * @return 所代表远程资源的响应结果 */ public static Map doPost(String httpUrl, Map map,String key,String vipara) { log.info(JSON.toJSONString(map)); map.put("data",Base64Utils.JsonToBase64((String) map.get("data"))); String jsonString = JSON.toJSONString(map); String Aesdata = AesUtils.encrypt(jsonString, key,vipara); HttpURLConnection connection = null; InputStream is = null; OutputStream os = null; BufferedReader br = null; String result = null; try { URL url = new URL(httpUrl); // 通过远程url连接对象打开连接 connection = (HttpURLConnection) url.openConnection(); // 设置连接请求方式 connection.setRequestMethod("POST"); // 设置连接主机服务器超时时间:15000毫秒 connection.setConnectTimeout(15000); // 设置读取主机服务器返回数据超时时间:60000毫秒 connection.setReadTimeout(60000); // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true connection.setDoOutput(true); // 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无 connection.setDoInput(true); // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。 connection.setRequestProperty("Content-Type", "application/json"); // 设置鉴权信息:Authorization: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0 // connection.setRequestProperty("Authorization", "Bearer // da3efcbf-0845-4fe3-8aba-ee040be542c0"); // 通过连接对象获取一个输出流 os = connection.getOutputStream(); // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的 os.write(Aesdata.getBytes()); // 通过连接对象获取一个输入流,向远程读取 if (connection.getResponseCode() == 200) { is = connection.getInputStream(); // 对输入流对象进行包装:charset根据工作项目组的要求来设置 br = new BufferedReader(new InputStreamReader(is, "UTF-8")); StringBuffer sbf = new StringBuffer(); String temp = null; // 循环遍历一行一行读取数据 while ((temp = br.readLine()) != null) { sbf.append(temp); sbf.append("\r\n"); } result = sbf.toString(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 关闭资源 if (null != br) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != os) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } // 断开与远程地址url的连接 connection.disconnect(); } String result1 = AesUtils.decrypt(result, key,vipara); Map map1 = JSON.parseObject(result1, Map.class); String data2 = (String) map1.get("data"); if ("0".equals(map1.get("code").toString())){ String s = Base64Utils.Base64ToJson(data2); map1.put("data",s); log.info(map1.toString()); return map1; } return null; }
/** * 手机短信登录 * @param phoneNumber * @param smsCode * @return */ public LoginUser smsLogin(String phoneNumber, String smsCode) { // 手机号或验证码为空 错误 if (StringUtils.isAnyEmpty(phoneNumber, smsCode)) { recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, "手机号/验证码必须填写"); throw new ServiceException("手机号/验证码必须填写"); } // 手机号输入错误 if (phoneNumber.length() != UserConstants.PHONE_NUMBER_LENGTH){ recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, "手机号输入错误"); throw new ServiceException("手机号输入错误,请检查"); } // 验证码输入错误 if (smsCode.length() != UserConstants.CODE_LENGTH){ recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, "验证码输入错误"); throw new ServiceException("验证码输入错误,请检查"); } //通过手机号查询用户信息 R<LoginUser> userResult = remoteUserService.getUserInfoByPhoneNumber(phoneNumber, SecurityConstants.INNER); if (R.FAIL == userResult.getCode()) { throw new ServiceException(userResult.getMsg()); } if (StringUtils.isNull(userResult.getData())) { recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, "登录手机号不存在"); throw new ServiceException("登录手机号:" + phoneNumber + " 不存在"); } LoginUser userInfo = userResult.getData(); //在redis中获取验证码 String cacheCode = redisService.getCacheObject(phoneNumber); if(cacheCode == null || ObjectUtils.isEmpty(cacheCode)) { recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, "验证码不存在或已过期"); throw new ServiceException("验证码不存在或已过期"); } if(cacheCode.equals(smsCode)){ //验证码正确,可删除验证码 redisService.deleteObject(phoneNumber); recordLogininfor(phoneNumber, Constants.LOGIN_SUCCESS, "登录成功"); } else { recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, "验证码输入有误"); throw new ServiceException("验证码输入有误"); } return userInfo; }
9.用户登录测试
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。