05Gulimall - mailbox registration and registration functions

05Gulimall - perfect mailbox registration and registration functions

Use mailbox verification code in project gulimall

It is divided into two services. One is the third-party service gulimall third party, which is used to really send verification codes. The second is the auth authentication service, which calls the third-party service to send the verification code.

1.gulimall-third-party

1.0 introducing dependencies

<!--SMS verification code-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

New package: component

New class: MyEmail

1.1 code of 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;

    /**
     * Send verification code
     *
     * @param email
     */
    public void sendMail(String email, String code) {

        MimeMessage mimeMessage = javaMailSender.createMimeMessage();

        try {
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
//            Set sender
            mimeMessageHelper.setFrom(from);
//            Set recipient
            mimeMessageHelper.setTo(email);
//            Set message subject
            mimeMessageHelper.setSubject("XXXXX Verification code for");
            //Generate random number
//            String random = randomInteger();
            //Place random numbers into the session
//            session.setAttribute("email",email);
//            session.setAttribute("code",random);

//            Set the style of the verification code
            mimeMessageHelper.setText("Hello, welcome to register XXXX Mall, your verification code is:<font style='color:green'>"+code+"</font>",true);

            javaMailSender.send(mimeMessage);

        } catch (MessagingException e) {
            e.printStackTrace();
        }


    }

    /**
     * Generate random verification code
     *
     * @return
     */
    private String randomInteger() {
        Random random = new Random();
        StringBuffer stringBuffer = new StringBuffer();
        //Generate 6-bit random numbers
        for (int i = 0;i<6;i++){
            int i1 = random.nextInt(10);
            stringBuffer.append(i1);
        }
        return stringBuffer.toString();
    }
}

1.2 modify the configuration file of gulimall third party

spring:
  mail:
    host: smtp.163.com
    username: xxxx@163.com
    password: xxx
    default-encoding: utf-8
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true
  • username: your email address
  • password: after you open PO3/SMTP in your mailbox, a string of passwords will be generated. Please specify Baidu

1.3 controller.SmsSendController

@RestController
@RequestMapping("/sms")
public class SmsSendController {
    @Autowired
    MyEmail myEmail;
    /**
     * Provided to other service calls
     * @param email
     * @param code
     * @return
     */
    @GetMapping("/sendCode")
    public R sendCode(@RequestParam("email") String email,@RequestParam("code") String code){
        myEmail.sendMail(email,code);
        return R.ok();
    }
}

2.gulimall-auth-server

Certification services

2.1 feign.ThirdPartFeignService

Call the service of remote 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("Verification code sending error!");
    }

}

2.3 Read timed out executing GET

This exception occurred:

Because the default timeout of Feign call is one minute, an exception will be thrown if the interface cannot return in one minute

Add the configuration in the application.yml file of gulimall auth server:

feign:
  client:
    config:
      default:
        connectTimeout: 10000
        readTimeout: 600000
        
spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true        

3. Front end reference

$(function () {
   $("#sendCode").click(function () {
   //2. Countdown
   if($(this).hasClass("disabled")) {
      //Countdown in progress
   } else {
      //1. Send verification code to specified mobile phone number
      $.get("/sms/sendCode?email=" + $("#phoneNum").val(),function (data) {
         if(data.code != 0) {
            alert(data.msg);
         }
      });
      timeoutChangeStyle();
   }
});
  });

Mainly sending requests:

$.get("/sms/sendCode?email=" + $("#phoneNum").val()

4. SMS sending interface anti brushing

4.1 further improve sending verification code

gulimall-auth-server

com.atguigu.gulimall.auth.controller.LoginController. Modify the sendCode method

To determine whether to send repeatedly within 60s, a system time can be spliced behind the verification code, such as underlined segmentation. Store it in redis.

Before sending each time, you need to determine whether redis contains this key? Even if this key is included, continue to judge whether the current system time minus the time stored in redis is less than 60000ms;

Finally, before sending the third-party service, restore the code to a 5-digit verification code to ensure that there is a 5-digit verification code in the email.

@ResponseBody
@GetMapping("/sms/sendCode")
public R sendCode(@RequestParam("email")String email){
    //TODO interface anti brush

    //First query whether redis contains
    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) {
            //It cannot be sent again in 60 seconds
            return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(),BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());
        }
    }

    //Mobile phone verification code verification, plus the current system time, is to verify whether it exceeds 60 seconds
    String code = MyEmail.randomInteger() + "_" + System.currentTimeMillis();
    String[] codeSplit = code.split("_");
    //Cache verification code
    redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+email,
            code,
            1,//1 minute expiration time
            TimeUnit.MINUTES);

    //Restore code to 5 bits
    code = codeSplit[0];
    R r = thirdPartFeignService.sendCode(email, code);
    if (r.getCode() == 0) {
        return R.ok();
    }else {
        return R.error("Verification code sending error!");
    }

}

4.2 improve the registration logic

Register method of com.atguigu.gulimall.auth.controller.LoginController

@PostMapping("/register")
public String regist(@Valid UserRegistVo userRegistVo,
                     BindingResult bindingResult,
                     RedirectAttributes redirectAttributes) {

    //Pre check
    if (bindingResult.hasErrors()) {
        Map<String, String> collect = bindingResult.getFieldErrors().stream()
                .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
        redirectAttributes.addFlashAttribute("errors", collect);
        return "redirect:http://auth.zuckmall.com/reg.html";
    }
    //Verification code
    //Verification code from the front end
    String codeFromUser = userRegistVo.getCode();
    //The verification code obtained from redis. Here is getPhone. I'm too lazy to change it to email
    String codeFromRedis = redisTemplate.opsForValue()
            .get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegistVo.getPhone());
    if (!StringUtils.isEmpty(codeFromRedis)) {
        //check
        String[] codeSplit = codeFromRedis.split("_");
        String codeFromRedisSplit = codeSplit[0];
        if (codeFromRedisSplit.equals(codeFromUser)) {
            //Verification code comparison succeeded
            //Delete verification code, token mechanism
            redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegistVo.getPhone());

            //TODO calls remote service registration

        }else {
            //The verification code is wrong and stored in the error set map
            Map<String, String> errors = new HashMap<>();
            errors.put("code","Verification code error");
            redirectAttributes.addFlashAttribute("errors", errors);
            //Verification error, return to the registration page
            return "redirect:http://auth.zuckmall.com/reg.html";
        }

    }else{
        //The verification code has expired and is stored in the error set map
        Map<String, String> errors = new HashMap<>();
        errors.put("code","Verification code error");
        redirectAttributes.addFlashAttribute("errors", errors);
        //Verification error, return to the registration page
        return "redirect:http://auth.zuckmall.com/reg.html";
    }


}

The main codes of verification code are as follows:

1. Obtain the verification code from redis

2. Judge whether it exists?

2.1 check on existence

2.1.1 split string

2.1.2 comparison

1) successful comparison: call remote service registration

2) comparison failure: an error prompt is returned (returned in the map)

2.2 if it does not exist, it means that the verification code has expired and will be prompted

	//Verification code
    //Verification code from the front end
    String codeFromUser = userRegistVo.getCode();
    //The verification code obtained from redis. Here is getPhone. I'm too lazy to change it to email
    String codeFromRedis = redisTemplate.opsForValue()
            .get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegistVo.getPhone());
    if (!StringUtils.isEmpty(codeFromRedis)) {
        //check
        String[] codeSplit = codeFromRedis.split("_");
        String codeFromRedisSplit = codeSplit[0];
        if (codeFromRedisSplit.equals(codeFromUser)) {
            //Verification code comparison succeeded
            //Delete verification code, token mechanism
            redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegistVo.getPhone());

            //TODO calls remote service registration

        }else {
            //The verification code is wrong and stored in the error set map
            Map<String, String> errors = new HashMap<>();
            errors.put("code","Verification code error");
            redirectAttributes.addFlashAttribute("errors", errors);
            //Verification error, return to the registration page
            return "redirect:http://auth.zuckmall.com/reg.html";
        }

    }else{
        //The verification code has expired and is stored in the error set map
        Map<String, String> errors = new HashMap<>();
        errors.put("code","Verification code error");
        redirectAttributes.addFlashAttribute("errors", errors);
        //Verification error, return to the registration page
        return "redirect:http://auth.zuckmall.com/reg.html";
    }

4.3 calling remote service registration

1. Previous code: TODO calls the code in the remote registration place

    .....
				//Call remote service registration
                R r = memberFeignService.regist(userRegistVo);
                if (r.getCode() == 0) {
                    //success
                    System.out.println("login was successful");
                    return "redirect:http://auth.zuckmall.com/login.html";
                }else{
                    System.err.println("There is a problem with the remote service");
                    Map<String, String> errors = 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

	/**
     * register
     * @return
     */
    @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();
        //Set default level
        MemberLevelEntity level = memberLevelDao.getDefaultLevel();
        member.setLevelId(level.getId());
        //Mobile phone, user name
        //First check whether it is unique and let the Controller perceive the exception mechanism
        checkPhoneUnique(vo.getPhone());
        checkUsernameUnique(vo.getUserName());
        member.setMobile(vo.getPhone());
        member.setUsername(vo.getUserName());
        //Password encrypted storage
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encode = encoder.encode(vo.getPassword());
        member.setPassword(encode);
        //preservation
        baseMapper.insert(member);
    }

	@Override
    public void checkPhoneUnique(String phone) throws PhoneExistException {
        Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));
        if (count > 0) {
            throw new PhoneExistException();
        }
    }

    @Override
    public void checkUsernameUnique(String username) throws UsernameExistException {
        Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", username));
        if (count > 0) {
            throw new UsernameExistException();
        }
    }

5,com.atguigu.member.dao.MemberLevelDao

@Mapper
public interface MemberLevelDao extends BaseMapper<MemberLevelEntity> {
    MemberLevelEntity getDefaultLevel();
}

6.MemberLevelDao.xml

<select id="getDefaultLevel" resultType="com.atguigu.member.entity.MemberLevelEntity">
    SELECT * FROM ums_member_level WHERE default_status = 1
</select>

Tags: Java Spring Cloud Project ribbon

Posted on Wed, 01 Dec 2021 12:02:51 -0500 by MeanMrMustard