【Easy云盘 | 第三篇】登录注册模块上篇(获取验证码、发送邮箱验证码、登录、注册、重置密码)基于AOP实现参数校验

在这里插入图片描述

文章目录

    • 4.2登录注册模块设计
      • 4.2.1获取验证码
        • (1)思路
        • (2)接口
        • (3)controller层
        • (4)CreateImageCodeUtils工具类
        • (5)测试结果
      • 4.2.2发送邮箱验证码
        • (1)接口
        • (2)controller层
        • (3)service层
      • 4.2.3基于AOP实现参数校验
        • (1)添加切面依赖
        • (2)自定义注解—标识哪些类需要被增强
        • (3)定义切面、切点、切点表达式、通知
      • 4.2.4登录
        • (1)登录接口
        • (2)controller层
        • (3)service层
      • 4.2.5注册
        • (1)接口
        • (2)controller层
        • (3)service层
      • 4.2.6重置密码
        • (1)接口
        • (2)controller层
        • (3)service层

4.2登录注册模块设计

4.2.1获取验证码

(1)思路
  • 思路:在登录页面和注册页面均为涉及到一个图片验证码的输入,防止用户拿到该接口去重复刷,避免系统崩溃
(2)接口
  • 获取验证码接口:Get请求

    http://localhost:7090/api/checkCode?type=0
    
  • 提交参数

    • type为0:获取登录注册页面的验证码
    • type为1:获取将要验证邮箱的验证码
    • 区别在于后端在返回session时,设置的key值不同,type为1即将要验证邮箱,当用户输入该验证码后,后端去session中取check_code_key_email为key的值与用户输入的验证码比较,而不是取注册页面的验证码与其比较

    image-20240401200408457

(3)controller层
	@RequestMapping(value = "/checkCode")
    public void checkCode(HttpServletResponse response, HttpSession session, Integer type) throws IOException {
        CreateImageCodeUtils vCode = new CreateImageCodeUtils(130, 38, 5, 10);
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");
        String code = vCode.getCode();
        if (type == null || type == 0) {
            //该验证码为登录注册页面的验证码
            session.setAttribute(Constants.CHECK_CODE_KEY, code);
        } else {
            //该验证码为验证邮箱的验证码
            session.setAttribute(Constants.CHECK_CODE_KEY_EMAIL, code);
        }
        vCode.write(response.getOutputStream());
    }
(4)CreateImageCodeUtils工具类
  • com.easy.entity.utils.CreateImageCodeUtils类(通用)
public class CreateImageCodeUtils {
    // 图片的宽度。
    private int width = 160;
    // 图片的高度。
    private int height = 40;
    // 验证码字符个数
    private int codeCount = 4;
    // 验证码干扰线数
    private int lineCount = 20;
    // 验证码
    private String code = null;
    // 验证码图片Buffer
    private BufferedImage buffImg = null;
    Random random = new Random();

    public CreateImageCodeUtils() {
        creatImage();
    }

    public CreateImageCodeUtils(int width, int height) {
        this.width = width;
        this.height = height;
        creatImage();
    }

    public CreateImageCodeUtils(int width, int height, int codeCount) {
        this.width = width;
        this.height = height;
        this.codeCount = codeCount;
        creatImage();
    }

    public CreateImageCodeUtils(int width, int height, int codeCount, int lineCount) {
        this.width = width;
        this.height = height;
        this.codeCount = codeCount;
        this.lineCount = lineCount;
        creatImage();
    }

    // 生成图片
    private void creatImage() {
        int fontWidth = width / codeCount;// 字体的宽度
        int fontHeight = height - 5;// 字体的高度
        int codeY = height - 8;

        // 图像buffer
        buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = buffImg.getGraphics();
        //Graphics2D g = buffImg.createGraphics();
        // 设置背景色
        g.setColor(getRandColor(200, 250));
        g.fillRect(0, 0, width, height);
        // 设置字体
        //Font font1 = getFont(fontHeight);
        Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
        g.setFont(font);

        // 设置干扰线
        for (int i = 0; i < lineCount; i++) {
            int xs = random.nextInt(width);
            int ys = random.nextInt(height);
            int xe = xs + random.nextInt(width);
            int ye = ys + random.nextInt(height);
            g.setColor(getRandColor(1, 255));
            g.drawLine(xs, ys, xe, ye);
        }

        // 添加噪点
        float yawpRate = 0.01f;// 噪声率
        int area = (int) (yawpRate * width * height);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            buffImg.setRGB(x, y, random.nextInt(255));
        }

        String str1 = randomStr(codeCount);// 得到随机字符
        this.code = str1;
        for (int i = 0; i < codeCount; i++) {
            String strRand = str1.substring(i, i + 1);
            g.setColor(getRandColor(1, 255));
            // g.drawString(a,x,y);
            // a为要画出来的东西,x和y表示要画的东西最左侧字符的基线位于此图形上下文坐标系的 (x, y) 位置处

            g.drawString(strRand, i * fontWidth + 3, codeY);
        }
    }

    // 得到随机字符
    private String randomStr(int n) {
        String str1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
        String str2 = "";
        int len = str1.length() - 1;
        double r;
        for (int i = 0; i < n; i++) {
            r = (Math.random()) * len;
            str2 = str2 + str1.charAt((int) r);
        }
        return str2;
    }

    // 得到随机颜色
    private Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色
        if (fc > 255) fc = 255;
        if (bc > 255) bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    /**
     * 产生随机字体
     */
    private Font getFont(int size) {
        Random random = new Random();
        Font font[] = new Font[5];
        font[0] = new Font("Ravie", Font.PLAIN, size);
        font[1] = new Font("Antique Olive Compact", Font.PLAIN, size);
        font[2] = new Font("Fixedsys", Font.PLAIN, size);
        font[3] = new Font("Wide Latin", Font.PLAIN, size);
        font[4] = new Font("Gill Sans Ultra Bold", Font.PLAIN, size);
        return font[random.nextInt(5)];
    }

    // 扭曲方法
    private void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private void shearX(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(2);

        boolean borderGap = true;
        int frames = 1;
        int phase = random.nextInt(2);

        for (int i = 0; i < h1; i++) {
            double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
            g.copyArea(0, i, w1, 1, (int) d, 0);
            if (borderGap) {
                g.setColor(color);
                g.drawLine((int) d, i, 0, i);
                g.drawLine((int) d + w1, i, w1, i);
            }
        }

    }

    private void shearY(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(40) + 10; // 50;

        boolean borderGap = true;
        int frames = 20;
        int phase = 7;
        for (int i = 0; i < w1; i++) {
            double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
            g.copyArea(i, 0, 1, h1, 0, (int) d);
            if (borderGap) {
                g.setColor(color);
                g.drawLine(i, (int) d, i, 0);
                g.drawLine(i, (int) d + h1, i, h1);
            }

        }

    }

    public void write(OutputStream sos) throws IOException {
        ImageIO.write(buffImg, "png", sos);
        sos.close();
    }

    public BufferedImage getBuffImg() {
        return buffImg;
    }

    public String getCode() {
        return code.toLowerCase();
    }
}
(5)测试结果

image-20240329162414791

4.2.2发送邮箱验证码

(1)接口
  • 发送邮箱验证码接口:Post

    http://localhost:7090/api/sendEmailCode
    
  • 编码格式:

    multipart/form-data
    
  • 请求参数:

    image-20240401200947362

(2)controller层
  • 获取session域中名为check_code_key_email的值,比较与用户输入的checkCode值是否相同,不同则输入图片验证码不正确
  • 相同则调用 EmailCodeService的sendEmailCode()方法
    @RequestMapping("/sendEmailCode")
    @GlobalInterceptor(checkLogin = false, checkParams = true)
    public ResponseVO sendEmailCode(HttpSession session,
                                    @VerifyParam(required = true, regex = VerifyRegexEnum.EMAIL, max = 150) String email,
                                    @VerifyParam(required = true) String checkCode,
                                    @VerifyParam(required = true) Integer type) {
        try {
            if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY_EMAIL))) {
                throw new BusinessException("图片验证码不正确");
            }
            emailCodeService.sendEmailCode(email, type);
            return getSuccessResponseVO(null);
        } finally {
            session.removeAttribute(Constants.CHECK_CODE_KEY_EMAIL);
        }
    }
(3)service层
  • EmailCodeServiceImpl的sendEmailCode方法
    • 首先若是注册用户,则校验邮箱是否存在
    • 通过工具类随机生成五位数;再执行发送邮箱的真正逻辑
	@Override
    @Transactional(rollbackFor = Exception.class)
    public void sendEmailCode(String toEmail, Integer type) {
        //如果是注册,校验邮箱是否已存在
        if (type == Constants.ZERO) {
            UserInfo userInfo = userInfoMapper.selectByEmail(toEmail);
            if (null != userInfo) {
                throw new BusinessException("邮箱已经存在");
            }
        }

        String code = StringTools.getRandomNumber(Constants.LENGTH_5);
        sendEmailCode(toEmail, code);

        emailCodeMapper.disableEmailCode(toEmail);
        EmailCode emailCode = new EmailCode();
        emailCode.setCode(code);
        emailCode.setEmail(toEmail);
        emailCode.setStatus(Constants.ZERO);
        emailCode.setCreateTime(new Date());
        emailCodeMapper.insert(emailCode);
    }
  • 发送邮箱的真正逻辑

    1. 导入依赖

      <!--邮件发送-->
      <dependency>
      	<groupId>org.springframework.boot</groupId>
      	<artifactId>spring-boot-starter-mail</artifactId>
      	<version>${springboot.version}</version>
      </dependency>
      
    2. 配置邮件发送者信息、密码

      # 配置邮件服务器的地址 smtp.qq.com
      spring.mail.host=smtp.qq.com
      # 配置邮件服务器的端口(465或587)
      spring.mail.port=587
      
      # 配置用户的账号
      spring.mail.username=改成自己的
      # 配置用户的密码
      spring.mail.password=改成自己的(推荐使用qq邮箱的授权登录密码)
      
    3. 使用自动注入的MimeMessageHelper对象、MimeMessage对象来发送邮件

      • 通过AppConfig类加载yml配置好的邮件发送者信息

      • 重要:Redis存放邮件中固定的格式,RedisComponent.getSysSettingsDto()、以及SysSettingDto类:

        	public SysSettingsDto getSysSettingsDto() {
                SysSettingsDto sysSettingsDto = (SysSettingsDto) redisUtils.get(Constants.REDIS_KEY_SYS_SETTING)
                        
                if (sysSettingsDto == null) {
                    sysSettingsDto = new SysSettingsDto();
                    redisUtils.set(Constants.REDIS_KEY_SYS_SETTING, sysSettingsDto);
                }
                return sysSettingsDto;
            }
        
        @JsonIgnoreProperties(ignoreUnknown = true)
        public class SysSettingsDto implements Serializable {
            /**
             * 注册发送邮件标题
             */
            private String registerEmailTitle = "【微网盘】—系统注册邮件";
        
            /**
             * 注册发送邮件内容
             */
            private String registerEmailContent = "您好,非常感谢您注册微网盘账号!您的验证码为 %s ,为了保证您账号的安全性,该验证码有效期为15分钟!";
        
            /**
             * 用户初始化空间大小 5M
             */
            private Integer userInitUseSpace = 5;
        }
        

        发送邮件核心代码

      private void sendEmailCode(String toEmail, String code) {
          try {
              MimeMessage message = javaMailSender.createMimeMessage();
      
              MimeMessageHelper helper = new MimeMessageHelper(message, true);
              //邮件发件人
              helper.setFrom(appConfig.getSendUserName());
              //邮件收件人 1或多个
              helper.setTo(toEmail);
      		
              //获取邮件内容格式
              SysSettingsDto sysSettingsDto = redisComponent.getSysSettingsDto();
      
              //邮件主题
              helper.setSubject(sysSettingsDto.getRegisterEmailTitle());
              //邮件内容
              helper.setText(String.format(sysSettingsDto.getRegisterEmailContent(), code));
              //邮件发送时间
              helper.setSentDate(new Date());
              
              //发送邮件
              javaMailSender.send(message);
      
          } catch (Exception e) {
              logger.error("邮件发送失败", e);
              throw new BusinessException("邮件发送失败");
          }
      }
      

4.2.3基于AOP实现参数校验

(1)添加切面依赖
		<!--切面-->
	    <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectjweaver.version}</version>
        </dependency>
(2)自定义注解—标识哪些类需要被增强
  • com.easypan.annotation.GlobalInterceptor
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface GlobalInterceptor {

    /**
     * 校验登录
     */
    boolean checkLogin() default true;

    /**
     * 校验参数
     */
    boolean checkParams() default false;

    /**
     * 校验管理员
     */
    boolean checkAdmin() default false;
}
(3)定义切面、切点、切点表达式、通知
  • com.easypan.aspect.GlobalOperationAspect
@Component("operationAspect")
@Aspect
public class GlobalOperationAspect {

    private static Logger logger = LoggerFactory.getLogger(GlobalOperationAspect.class);
    private static final String TYPE_STRING = "java.lang.String";
    private static final String TYPE_INTEGER = "java.lang.Integer";
    private static final String TYPE_LONG = "java.lang.Long";

    @Resource
    private UserInfoService userInfoService;

    @Resource
    private AppConfig appConfig;

    /**
     * @Description: 1、requestInterceptor()方法为切点
     *                2、@annotation(com.easypan.annotation.GlobalInterceptor) 为切点表达式
     *                3、只有在执行带有 @GlobalInterceptor注解的方法时,才会触发 requestInterceptor()方法中的逻辑
     */
    @Pointcut("@annotation(com.easypan.annotation.GlobalInterceptor)")
    private void requestInterceptor() {
    }

    //通知
    @Before("requestInterceptor()")
    public void interceptorDo(JoinPoint point) throws BusinessException {
        try {
            Object target = point.getTarget();      //返回被代理的目标对象,即拦截器拦截的目标方法所属的对象实例
            Object[] arguments = point.getArgs();   //方法返回被拦截方法的参数数组

            String methodName = point.getSignature().getName();                             //获取被拦截方法的 Method 对象的名称
            Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();   //返回方法的参数类型数组

            Method method = target.getClass().getMethod(methodName, parameterTypes);        //通过method名称和参数数组获取 方法对象
            GlobalInterceptor interceptor = method.getAnnotation(GlobalInterceptor.class);  //获取该方法上的GlobalInterceptor注解
            if (null == interceptor) {
                return;
            }
            /**
             * 检查是否需要 校验登录
             * 当GlobalInterceptor注解的checkLogin的属性为True 或 checkAdmin属性为true,即需要检验登录
             */
            if (interceptor.checkLogin() || interceptor.checkAdmin()) {
                checkLogin(interceptor.checkAdmin());
            }
            /**
             * 检查是否需要 校验请求路径参数
             * 当GlobalInterceptor注解的checkParams的属性为True,即需要检验请求路径参数
             */
            if (interceptor.checkParams()) {
                validateParams(method, arguments);
            }

        } catch (BusinessException e) {
            logger.error("全局拦截器异常", e);
            throw e;
        } catch (Exception e) {
            logger.error("全局拦截器异常", e);
            throw new BusinessException(ResponseCodeEnum.CODE_500);
        } catch (Throwable e) {
            logger.error("全局拦截器异常", e);
            throw new BusinessException(ResponseCodeEnum.CODE_500);
        }
    }
}
  • 校验登录: checkLogin(Boolean checkAdmin)
	//校验登录
    private void checkLogin(Boolean checkAdmin) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        SessionWebUserDto sessionUser = (SessionWebUserDto) session.getAttribute(Constants.SESSION_KEY);

        //果当前处于开发模式下,并且没有用户登录信息,则尝试自动登录第一个查询到的用户信息
        if (sessionUser == null && appConfig.getDev() != null && appConfig.getDev()) {
            List<UserInfo> userInfoList = userInfoService.findListByParam(new UserInfoQuery());
            if (!userInfoList.isEmpty()) {
                UserInfo userInfo = userInfoList.get(0);
                sessionUser = new SessionWebUserDto();
                sessionUser.setUserId(userInfo.getUserId());
                sessionUser.setNickName(userInfo.getNickName());
                sessionUser.setAdmin(true);
                session.setAttribute(Constants.SESSION_KEY, sessionUser);
            }
        }
        
        //查到的sessionUser仍为空
        if (null == sessionUser) {
            throw new BusinessException(ResponseCodeEnum.CODE_901); //登录超时
        }

        if (checkAdmin && !sessionUser.getAdmin()) {
            throw new BusinessException(ResponseCodeEnum.CODE_404);//普通用户访问管理员页面,请求地址不存在
        }
    }
  • 校验请求参数validateParams

  • 注意:补充@VerifyParam注解:com.easypan.annotation.VerifyParam

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.PARAMETER, ElementType.FIELD})
    public @interface VerifyParam {
        /**
         * 校验正则
         *
         * @return
         */
        VerifyRegexEnum regex() default VerifyRegexEnum.NO;
    
        /**
         * 最小长度
         *
         * @return
         */
        int min() default -1;
    
        /**
         * 最大长度
         *
         * @return
         */
        int max() default -1;
    
        boolean required() default false;
    }
    
 	/**
     * @Description: 校验方法的参数
     * @Parm: Method m:增强方法对象;Object[] arguments:增强方法的参数数组
     */
    private void validateParams(Method m, Object[] arguments) throws BusinessException {
        Parameter[] parameters = m.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object value = arguments[i];
            VerifyParam verifyParam = parameter.getAnnotation(VerifyParam.class);   //获取每个参前面的@VerifyParam注解
            if (verifyParam == null) {
                //没有加@VerifyParam注解
                continue;
            }
            //基本数据类型
            if (TYPE_STRING.equals(parameter.getParameterizedType().getTypeName()) || TYPE_LONG.equals(parameter.getParameterizedType().getTypeName()) || TYPE_INTEGER.equals(parameter.getParameterizedType().getTypeName())) {
                checkValue(value, verifyParam);
            } else {
                //如果传递的是对象
                checkObjValue(parameter, value);
            }
        }
    }
  • 校验基本数据类型
	/**
     * 校验参数
     */
    private void checkValue(Object value, VerifyParam verifyParam) throws BusinessException {
        Boolean isEmpty = value == null || StringUtils.isEmpty(value.toString());
        Integer length = value == null ? 0 : value.toString().length();

        /**
         * 校验空
         */
        if (isEmpty && verifyParam.required()) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }

        /**
         * 校验长度
         */
        if (!isEmpty && (verifyParam.max() != -1 && verifyParam.max() < length || verifyParam.min() != -1 && verifyParam.min() > length)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        /**
         * 校验正则
         */
        if (!isEmpty && !StringUtils.isEmpty(verifyParam.regex().getRegex()) && !VerifyUtils.verify(verifyParam.regex(), String.valueOf(value))) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
    }
  • 校验对象
	private void checkObjValue(Parameter parameter, Object value) {
        try {
            String typeName = parameter.getParameterizedType().getTypeName();
            Class classz = Class.forName(typeName);
            Field[] fields = classz.getDeclaredFields();
            for (Field field : fields) {
                VerifyParam fieldVerifyParam = field.getAnnotation(VerifyParam.class);
                if (fieldVerifyParam == null) {
                    continue;
                }
                field.setAccessible(true);
                Object resultValue = field.get(value);
                checkValue(resultValue, fieldVerifyParam);
            }
        } catch (BusinessException e) {
            logger.error("校验参数失败", e);
            throw e;
        } catch (Exception e) {
            logger.error("校验参数失败", e);
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
    }

4.2.4登录

(1)登录接口
  • 登录接口:Post请求

    http://localhost:7090/api/login
    
  • 编码格式:

    multipart/form-data
    
  • 提交参数:

    image-20240401195937412

(2)controller层
	/**
     * @Description: 登录
     */
    @RequestMapping("/login")
    @GlobalInterceptor(checkLogin = false, checkParams = true)
    public ResponseVO login(HttpSession session, HttpServletRequest request,
                            @VerifyParam(required = true) String email,
                            @VerifyParam(required = true) String password,
                            @VerifyParam(required = true) String checkCode) {
        try {
            if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY))) {
                throw new BusinessException("图片验证码不正确");
            }
            SessionWebUserDto sessionWebUserDto = userInfoService.login(email, password);
            session.setAttribute(Constants.SESSION_KEY, sessionWebUserDto);
            return getSuccessResponseVO(sessionWebUserDto);
        } finally {
            session.removeAttribute(Constants.CHECK_CODE_KEY);
        }
    }
(3)service层
	@Override
    public SessionWebUserDto login(String email, String password) {
        UserInfo userInfo = this.userInfoMapper.selectByEmail(email);
        if (null == userInfo || !userInfo.getPassword().equals(password)) {
            throw new BusinessException("账号或者密码错误");
        }
        if (UserStatusEnum.DISABLE.getStatus().equals(userInfo.getStatus())) {
            throw new BusinessException("账号已禁用");
        }
        UserInfo updateInfo = new UserInfo();
        updateInfo.setLastLoginTime(new Date());
        this.userInfoMapper.updateByUserId(updateInfo, userInfo.getUserId());
        SessionWebUserDto sessionWebUserDto = new SessionWebUserDto();
        sessionWebUserDto.setNickName(userInfo.getNickName());
        sessionWebUserDto.setUserId(userInfo.getUserId());
        if (ArrayUtils.contains(appConfig.getAdminEmails().split(","), email)) {
            sessionWebUserDto.setAdmin(true);
        } else {
            sessionWebUserDto.setAdmin(false);
        }
        //用户空间
        UserSpaceDto userSpaceDto = new UserSpaceDto();
        userSpaceDto.setUseSpace(fileInfoService.getUserUseSpace(userInfo.getUserId()));
        userSpaceDto.setTotalSpace(userInfo.getTotalSpace());
        redisComponent.saveUserSpaceUse(userInfo.getUserId(), userSpaceDto);
        return sessionWebUserDto;
    }

4.2.5注册

(1)接口
  • 注册接口:Post

    http://localhost:7090/api/register
    
  • 编码格式:

    multipart/form-data
    
  • 请求参数:

    image-20240401201421458

(2)controller层
	@RequestMapping("/register")
    @GlobalInterceptor(checkLogin = false, checkParams = true)
    public ResponseVO register(HttpSession session,
                               @VerifyParam(required = true, regex = VerifyRegexEnum.EMAIL, max = 150) String email,
                               @VerifyParam(required = true, max = 20) String nickName,
                               @VerifyParam(required = true, regex = VerifyRegexEnum.PASSWORD, min = 8, max = 18) String password,
                               @VerifyParam(required = true) String checkCode,
                               @VerifyParam(required = true) String emailCode) {
        try {
            if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY))) {
                throw new BusinessException("图片验证码不正确");
            }
            userInfoService.register(email, nickName, password, emailCode);
            return getSuccessResponseVO(null);
        } finally {
            session.removeAttribute(Constants.CHECK_CODE_KEY);
        }
    }
(3)service层
  • 检验邮箱是否已经存在
  • 检验昵称是否已经存在
  • 校验邮箱验证码 :
	@Override
    @Transactional(rollbackFor = Exception.class)
    public void register(String email, String nickName, String password, String emailCode) {
        UserInfo userInfo = this.userInfoMapper.selectByEmail(email);
        if (null != userInfo) {
            throw new BusinessException("邮箱账号已经存在");
        }
        UserInfo nickNameUser = this.userInfoMapper.selectByNickName(nickName);
        if (null != nickNameUser) {
            throw new BusinessException("昵称已经存在");
        }
        //校验邮箱验证码
        emailCodeService.checkCode(email, emailCode);
        String userId = StringUtils.getRandomNumber(Constants.LENGTH_10);
        userInfo = new UserInfo();
        userInfo.setUserId(userId);
        userInfo.setNickName(nickName);
        userInfo.setEmail(email);
        userInfo.setPassword(StringUtils.encodeByMD5(password));
        userInfo.setJoinTime(new Date());
        userInfo.setStatus(UserStatusEnum.ENABLE.getStatus());
        SysSettingsDto sysSettingsDto = redisComponent.getSysSettingsDto();
        userInfo.setTotalSpace(sysSettingsDto.getUserInitUseSpace() * Constants.MB);
        userInfo.setUseSpace(0L);
        this.userInfoMapper.insert(userInfo);
    }
	@Override
    public void checkCode(String email, String code) {
        EmailCode emailCode = emailCodeMapper.selectByEmailAndCode(email, code);
        if (null == emailCode) {
            throw new BusinessException("邮箱验证码不正确");
        }
        if (emailCode.getStatus() == 1 || System.currentTimeMillis() - emailCode.getCreateTime().getTime() > Constants.LENGTH_15 * 1000 * 60) {
            throw new BusinessException("邮箱验证码已失效");
        }
        emailCodeMapper.disableEmailCode(email);
    }

4.2.6重置密码

(1)接口
  • 重置密码接口:

    http://localhost:7090/api/resetPwd
    
  • 编码格式:

    multipart/form-data
    
  • 请求参数:

    image-20240401202056500

(2)controller层
	@RequestMapping("/resetPwd")
    @GlobalInterceptor(checkLogin = false, checkParams = true)
    public ResponseVO resetPwd(HttpSession session,
                               @VerifyParam(required = true, regex = VerifyRegexEnum.EMAIL, max = 150) String email,
                               @VerifyParam(required = true, regex = VerifyRegexEnum.PASSWORD, min = 8, max = 18) String password,
                               @VerifyParam(required = true) String checkCode,
                               @VerifyParam(required = true) String emailCode) {
        try {
            if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY))) {
                throw new BusinessException("图片验证码不正确");
            }
            userInfoService.resetPwd(email, password, emailCode);
            return getSuccessResponseVO(null);
        } finally {
            session.removeAttribute(Constants.CHECK_CODE_KEY);
        }
    }
(3)service层
	@Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPwd(String email, String password, String emailCode) {
        UserInfo userInfo = this.userInfoMapper.selectByEmail(email);
        if (null == userInfo) {
            throw new BusinessException("邮箱账号不存在");
        }
        //校验邮箱验证码
        emailCodeService.checkCode(email, emailCode);

        UserInfo updateInfo = new UserInfo();
        updateInfo.setPassword(StringUtils.encodeByMD5(password));
        this.userInfoMapper.updateByEmail(updateInfo, email);
    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/524111.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Chapter 1 Basic Concepts of Communication and Communication Systems

1.1 The Concept of Communication communication【通信】:It is the process of using signals to transmit messages containing information in space. To put it simply, communication is the spatial transmission of information【信息的空间传递】Information【信息】…

LeetCode-46. 全排列【数组 回溯】

LeetCode-46. 全排列【数组 回溯】 题目描述&#xff1a;解题思路一&#xff1a;回溯。回溯三部曲解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案…

信息收集之内网渗透(二)

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 0、前言 本文主要是一些命令的集合&#xff0c;会比较枯…

PCL 点到三角形的距离(3D)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 给定三角形ABC和点P,设Q为描述ABC上离P最近的点。求Q的一个方法:如果P在ABC内,那么P的正交投影点就是离P最近的点Q。如果P投影在ABC之外,最近的点则必须位于它的一条边上。在这种情况下,Q可以通过计算线段AB、…

【算法】动态规划练习(一)

目录 1137. 第 N 个泰波那契数 分析 代码 面试题 08.01. 三步问题 分析 代码 746. 使用最小花费爬楼梯 分析 代码 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c;请返回第 n 个泰波…

Redis: 持久化

文章目录 一、RDB持久化1、概念2、执行时机&#xff08;1&#xff09; 执行save命令&#xff08;2&#xff09;执行bgsave命令&#xff08;3&#xff09;Redis停机时&#xff08;4&#xff09;触发RDB条件 3、原理4、小结 二、AOF持久化1、概念2、AOF配置3、AOF文件重写4、RDB与…

中文地址分词器源码阅读(jiedi)

文章目录 structure.p文件pd.read_excelenumerate思维导图核心源码讲解jiedi.pytrain.py 总结 structure 点击左边的Structure按钮就如Structure界面。从Structure我们可以看出当前代码文件中有多少个全局变量、函数、类以及类中有多少个成员变量和成员函数。 其中V图标表示全…

HFSS仿真环形耦合器学习笔记

HFSS仿真环形耦合器学习笔记 文章目录 HFSS仿真环形耦合器学习笔记1、 理论基础2、 设计分析3、 仿真验证1、 求解器设置2、 建模3、 激励方式设置4、 边界条件设置5、 扫频设置6、 设计检查&#xff0c;仿真分析7、 数据后处理 1、 理论基础 环形定向耦合器的结构示意图如图所…

三分钟带你了解,可重构柔性装配生产线

产品个性化时代&#xff0c;产品小批量、多批次&#xff0c;行业常用高柔性的人-机混合装配线实现跨品类产品装配&#xff0c;但产品的装配质量一致性差、效率低成为行业痛点。富唯智能联合清华大学提出了可重构柔性装配方法和技术&#xff0c;实现跨品类产品的数控自动化装配。…

Spring 源码学习笔记(一)之搭建源码环境

前言 一直以来对 Spring 源码的理解不够全面&#xff0c;也不成条理&#xff0c;只是对其中的某小部分比较了解&#xff0c;所以从今天开始要重新系统学习 Spring 的源码了。 搭建源码环境 首先需要说明的是&#xff0c;源码环境并不是必须的&#xff0c;搭建源码环境唯一的好…

代码随想录算法训练营第四十六天 |139. 单词拆分 、卡码网56. 携带矿石资源

代码随想录算法训练营第四十六天 |139. 单词拆分 、卡码网56. 携带矿石资源 139. 单词拆分题目解法 卡码网56. 携带矿石资源题目解法 背包总结感悟 139. 单词拆分 题目 解法 题解链接 1. class Solution { public:bool wordBreak(string s, vector<string>& wordD…

JUC_1

进程 概述 进程&#xff1a;程序是静止的&#xff0c;进程实体的运行过程就是进程&#xff0c;是系统进行资源分配的基本单位 进程的特征&#xff1a;并发性、异步性、动态性、独立性、结构性 线程&#xff1a;线程是属于进程的&#xff0c;是一个基本的 CPU 执行单元&#x…

主机名控制者:DNS服务器

文章目录 什么是DNS用网络主机名取得IP的历史渊源DNS的主机名对应IP的查询流程合法DNS的关键&#xff0c;申请区域查询授权DNS数据库的记录&#xff1a;正解、反解、Zone的意义DNS数据库的类型&#xff1a;hint、Master/Slave架构 Client端的设置相关配置文件DNS的正、反解查询…

deepin 社区月报 | 2024年3月,多款应用重磅上新

内容来源&#xff1a;deepin 社区 deepin&#xff08;深度&#xff09;社区3月总览 2024年3月&#xff0c;有968位小伙伴加入了deepin&#xff08;深度&#xff09;社区大家庭&#xff0c;目前共有论坛伙伴152,779位&#xff1b; 在3月&#xff0c;deepin V23 Beta3共进行了5…

python 04字典映射

1.创建字典 &#xff08;1&#xff09;通过自己的输入创建字典 字典用大括号&#xff0c;至此&#xff0c;小括号( )表示元组&#xff0c;中括号[ ]表示列表&#xff0c;大括号{ }表示字典&#xff0c;python中最常用的三种数据结构就全了 &#xff08;2&#xff09;通过其他…

【C++进阶】哈希思想之哈希表和哈希桶模拟实现unordered_map和unordered_set

哈希表和哈希桶 一&#xff0c;什么是哈希二&#xff0c;关联式容器unordered_map/set1. unordered_map2. unordered_set 三&#xff0c;哈希的结构1. 哈希函数2. 哈希冲突 四&#xff0c;哈希表&#xff08;闭散列&#xff09;及其模拟实现五&#xff0c;哈希桶&#xff08;开…

练手项目层高阶3—《详解文件版本——通讯录管理系统》

文章目录 &#x1f6a9;前言&#x1f9e9;框架结构&#x1f4fa;效果展示&#x1f680;完整代码 &#x1f6a9;前言 我们前面写的两种方法&#xff08;静态和动态)&#xff0c;唯一缺点就是每次运行都要输入新的数据&#xff0c;很麻烦&#xff0c;也就是说写入的数据无法长久保…

树莓派5使用体验

原文地址&#xff1a;树莓派5使用体验 - Pleasure的博客 下面是正文内容&#xff1a; 前言 好久没有关于教程方面的博文了&#xff0c;由于最近打算入门嵌入式系统&#xff0c;所以就去购入了树莓派5开发板 树莓派5是2023年10月23日正式发售的&#xff0c;过去的时间不算太远吧…

XML文档节点导航与选择指南 | XPath基础知识

XPath&#xff08;XML Path Language&#xff09;是XSLT标准的主要组成部分。它用于在XML文档中浏览元素和属性&#xff0c;提供了一种强大的定位和选择节点的方式。 XPath的基本特点 代表XML路径语言&#xff1a; XPath是一种用于在XML文档中导航和选择节点的语言。 路径样式…

【CSS】新闻页面制作 案例一

&#xff08;大家好&#xff0c;今天我们将通过案例实战对之前学习过的CSS知识进行复习巩固&#xff0c;大家和我一起来吧&#xff0c;加油&#xff01;&#x1f495;&#xff09; 目录 一、前述 二、素材 案例文字素材 案例图片素材 三、案例分析 四、案例实施 五、应用…