1.gulimall-third-party 1.0 引入依赖在项目gulimall中使用邮箱验证码
分为两个服务,一个是第三方服务gulimall-third-party,用于真正发送验证码。第二个是auth认证服务,调用第三方服务发送验证码。
org.springframework.boot spring-boot-starter-mail
新建包:component
新建类:MyEmail
1.1 MyEmail的代码package com.atguigu.thirdparty.component; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import java.util.Random; @Component public class MyEmail { @Value("${spring.mail.username}") private String from; @Resource JavaMailSender javaMailSender; public void sendMail(String email, String code) { MimeMessage mimeMessage = javaMailSender.createMimeMessage(); try { MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true); // 设置发件人 mimeMessageHelper.setFrom(from); // 设置收件人 mimeMessageHelper.setTo(email); // 设置邮件主题 mimeMessageHelper.setSubject("XXXXX的验证码"); //生成随机数 // String random = randomInteger(); //将随机数放置到session中 // session.setAttribute("email",email); // session.setAttribute("code",random); // 设置验证码的样式 mimeMessageHelper.setText("你好,欢迎注册XXXX商城,您的验证码是:"+code+"",true); javaMailSender.send(mimeMessage); } catch (MessagingException e) { e.printStackTrace(); } } private String randomInteger() { Random random = new Random(); StringBuffer stringBuffer = new StringBuffer(); //生成6位的随机数 for (int i = 0;i<6;i++){ int i1 = random.nextInt(10); stringBuffer.append(i1); } return stringBuffer.toString(); } }1.2 修改gulimall-third-party的配置文件
spring: mail: host: smtp.163.com username: [email protected] password: xxx default-encoding: utf-8 properties: mail: smtp: auth: true starttls: enable: true required: true
- username: 你的发件邮箱
- password:你的邮箱开启PO3/SMTP后会生成一串密码,具体百度
@RestController @RequestMapping("/sms") public class SmsSendController { @Autowired MyEmail myEmail; @GetMapping("/sendCode") public R sendCode(@RequestParam("email") String email,@RequestParam("code") String code){ myEmail.sendMail(email,code); return R.ok(); } }2.gulimall-auth-server
认证服务
2.1 feign.ThirdPartFeignService调用远程gulimall-third-party的服务
@FeignClient("gulimall-third-party") public interface ThirdPartFeignService { @GetMapping("/sms/sendCode") R sendCode(@RequestParam("email") String email, @RequestParam("code") String code); }2.2 controller.LoginController
@ResponseBody @GetMapping("/sms/sendCode") public R sendCode(@RequestParam("email")String email){ String code = MyEmail.randomInteger(); R r = thirdPartFeignService.sendCode(email, code); if (r.getCode() == 0) { return R.ok(); }else { return R.error("验证码发送错误!"); } }2.3 Read timed out executing GET
出现这个异常:
因为Feign调用默认的超时时间为一分钟,一分钟接口不能返回就会抛出异常
在gulimall-auth-server的application.yml文件加入配置:
feign: client: config: default: connectTimeout: 10000 readTimeout: 600000 spring: cloud: loadbalancer: retry: enabled: true3.前端参考
$(function () { $("#sendCode").click(function () { //2、倒计时 if($(this).hasClass("disabled")) { //正在倒计时中 } else { //1、给指定手机号发送验证码 $.get("/sms/sendCode?email=" + $("#phoneNum").val(),function (data) { if(data.code != 0) { alert(data.msg); } }); timeoutChangeStyle(); } }); });
主要是发送请求:
$.get("/sms/sendCode?email=" + $("#phoneNum").val()
4.短信发送接口防刷 4.1 进一步完善发送验证码gulimall-auth-server
com.atguigu.gulimall.auth.controller.LoginController,修改这个sendCode方法
判断是否在60s内重复发送,可以在验证码后面拼接一个系统时间,比如说用下划线分割。将其存储在redis中。
每次发送前需要判断redis中是否含有这个key?即便是含有这个key也要继续判断,当前系统时间减去redis中存储的时间是否小于60000ms;
最后注意,发送第三方服务前,把code还原成5位验证码,保证邮件里是5位验证码。
@ResponseBody @GetMapping("/sms/sendCode") public R sendCode(@RequestParam("email")String email){ //TODO 接口防刷 //先查询redis中是否含有 String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + email); if (!StringUtils.isEmpty(redisCode)) { String[] split = redisCode.split("_"); long timeFromRedis = Long.parseLong(split[1]); if (System.currentTimeMillis() - timeFromRedis <= 60000) { //60秒不可再发 return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(),BizCodeEnum.SMS_CODE_EXCEPTION.getMsg()); } } //手机验证码校验,加上系统当前时间是为了验证是否超过60秒 String code = MyEmail.randomInteger() + "_" + System.currentTimeMillis(); String[] codeSplit = code.split("_"); //缓存验证码 redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+email, code, 1,//1分钟过期时间 TimeUnit.MINUTES); //将code还原成5位 code = codeSplit[0]; R r = thirdPartFeignService.sendCode(email, code); if (r.getCode() == 0) { return R.ok(); }else { return R.error("验证码发送错误!"); } }4.2 完善注册逻辑
com.atguigu.gulimall.auth.controller.LoginController的regist方法
@PostMapping("/register") public String regist(@Valid UserRegistVo userRegistVo, BindingResult bindingResult, RedirectAttributes redirectAttributes) { //前置校验 if (bindingResult.hasErrors()) { Mapcollect = bindingResult.getFieldErrors().stream() .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage)); redirectAttributes.addFlashAttribute("errors", collect); return "redirect:http://auth.zuckmall.com/reg.html"; } //校验验证码 //前端传来的验证码 String codeFromUser = userRegistVo.getCode(); //从redis中获取的验证码,这里是getPhone,懒得改成email了 String codeFromRedis = redisTemplate.opsForValue() .get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegistVo.getPhone()); if (!StringUtils.isEmpty(codeFromRedis)) { //校验 String[] codeSplit = codeFromRedis.split("_"); String codeFromRedisSplit = codeSplit[0]; if (codeFromRedisSplit.equals(codeFromUser)) { //验证码对比成功 //删除验证码,令牌机制 redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegistVo.getPhone()); //TODO 调用远程服务注册 }else { //验证码错误,存入错误集合map中 Map errors = new HashMap<>(); errors.put("code","验证码错误"); redirectAttributes.addFlashAttribute("errors", errors); //校验出错,回到注册页 return "redirect:http://auth.zuckmall.com/reg.html"; } }else{ //验证码过期了,存入错误集合map中 Map errors = new HashMap<>(); errors.put("code","验证码错误"); redirectAttributes.addFlashAttribute("errors", errors); //校验出错,回到注册页 return "redirect:http://auth.zuckmall.com/reg.html"; } }
校验验证码的主要代码如下:
1.从redis中获取验证码
2.判断是否存在?
2.1 存在就校验
2.1.1 分割字符串
2.1.2 比对
1)比对成功:调用远程服务注册
2)比对失败:返回错误提示(map中返回)
2.2 不存在就说明验证码过期,给予提示
//校验验证码 //前端传来的验证码 String codeFromUser = userRegistVo.getCode(); //从redis中获取的验证码,这里是getPhone,懒得改成email了 String codeFromRedis = redisTemplate.opsForValue() .get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegistVo.getPhone()); if (!StringUtils.isEmpty(codeFromRedis)) { //校验 String[] codeSplit = codeFromRedis.split("_"); String codeFromRedisSplit = codeSplit[0]; if (codeFromRedisSplit.equals(codeFromUser)) { //验证码对比成功 //删除验证码,令牌机制 redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegistVo.getPhone()); //TODO 调用远程服务注册 }else { //验证码错误,存入错误集合map中 Map4.3 调用远程服务注册errors = new HashMap<>(); errors.put("code","验证码错误"); redirectAttributes.addFlashAttribute("errors", errors); //校验出错,回到注册页 return "redirect:http://auth.zuckmall.com/reg.html"; } }else{ //验证码过期了,存入错误集合map中 Map errors = new HashMap<>(); errors.put("code","验证码错误"); redirectAttributes.addFlashAttribute("errors", errors); //校验出错,回到注册页 return "redirect:http://auth.zuckmall.com/reg.html"; }
1.上一个代码:TODO调用远程注册地方的代码完善
..... //调用远程服务注册 R r = memberFeignService.regist(userRegistVo); if (r.getCode() == 0) { //成功 System.out.println("注册成功"); return "redirect:http://auth.zuckmall.com/login.html"; }else{ System.err.println("远程服务存在问题"); Maperrors = new HashMap<>(); errors.put("msg", String.valueOf(r.get("msg"))); redirectAttributes.addFlashAttribute("errors", errors); return "redirect:http://auth.zuckmall.com/reg.html"; } .....
2.MemberFeignService
@FeignClient("gulimall-member") public interface MemberFeignService { @PostMapping("/member/member/regist") R regist(@RequestBody UserRegistVo vo); }
3.com.atguigu.member.controller.MemberController
@PostMapping("/regist") public R regist(@RequestBody MemberRegistVo vo){ try { memberService.regist(vo); } catch (PhoneExistException e) { e.printStackTrace(); return R.error(BizCodeEnum.PHONE_EXIST_EXCEPTION.getCode(), BizCodeEnum.PHONE_EXIST_EXCEPTION.getMsg()); } catch (UsernameExistException e){ e.printStackTrace(); return R.error(BizCodeEnum.USER_EXIST_EXCEPTION.getCode(), BizCodeEnum.USER_EXIST_EXCEPTION.getMsg()); } return R.ok(); }
4.com.atguigu.member.service.impl.MemberServiceImpl
@Override public void regist(MemberRegistVo vo) { MemberEntity member = new MemberEntity(); //设置默认等级 MemberLevelEntity level = memberLevelDao.getDefaultLevel(); member.setLevelId(level.getId()); //手机,用户名 //先检查是否唯一,让Controller感知异常,异常机制 checkPhoneUnique(vo.getPhone()); checkUsernameUnique(vo.getUserName()); member.setMobile(vo.getPhone()); member.setUsername(vo.getUserName()); //密码加密存储 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String encode = encoder.encode(vo.getPassword()); member.setPassword(encode); //保存 baseMapper.insert(member); } @Override public void checkPhoneUnique(String phone) throws PhoneExistException { Integer count = baseMapper.selectCount(new QueryWrapper().eq("mobile", phone)); if (count > 0) { throw new PhoneExistException(); } } @Override public void checkUsernameUnique(String username) throws UsernameExistException { Integer count = baseMapper.selectCount(new QueryWrapper ().eq("username", username)); if (count > 0) { throw new UsernameExistException(); } }
5、com.atguigu.member.dao.MemberLevelDao
@Mapper public interface MemberLevelDao extends baseMapper{ MemberLevelEntity getDefaultLevel(); }
6.MemberLevelDao.xml
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)