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

news2024/10/7 13:26:22

在这里插入图片描述

文章目录

    • 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);
    }

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++初阶---vector(STL)

1、vector的介绍和使用 1.1、vector的介绍 1. vector是表示可变大小数组的序列容器。 2. 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是…

Pagerank学习笔记

前言 通过b站视频学习以及网上资料整理后进行学习笔记的撰写&#xff0c;只包含pr的一些简单原理及其应用。不包括pr的自定义实现。 一、pagerank简介 背景简介 PageRank算法 由Google创始人Larry Page 在斯坦福读大学时提出&#xff0c;又称PR,佩奇排名。主要针对网页进行…

Docker容器(五)Docker Compose

一、概述 1.1介绍 Docker Compose是Docker官方的开源项目&#xff0c;负责实现对Docker容器集群的快速编排。Compose 是 Docker 公司推出的一个工具软件&#xff0c;可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml&#xff0c;…

bootstrap+thymeleaf 页面多选回显时莫名其妙多了

bootstrapthymeleaf 页面多选回显时莫名其妙多了 问题现象问题分析问题处理总结 问题现象 今天遇到的问题的描述正如标题中的一样&#xff0c;就是后台管理系统在配置完内容后点击保存&#xff0c;回显时发现页面竟然莫名其妙多了一些数据。项目整体后台管理系统采用的是boots…

分类预测 | Matlab实现DRN深度残差网络数据分类预测

分类预测 | Matlab实现DRN深度残差网络数据分类预测 目录 分类预测 | Matlab实现DRN深度残差网络数据分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现DRN深度残差网络数据分类预测&#xff08;完整源码和数据&#xff09;&#xff0c;运行环境为Matl…

usb_camera传输视频流编码的问题记录!

前言&#xff1a; 大家好&#xff0c;今天给大家分享的内容是&#xff0c;一个vip课程付费的朋友&#xff0c;在学习过程中遇到了一个usb采集的视频数据流&#xff0c;经过ffmpeg编码&#xff0c;出现了问题&#xff1a; 问题分析&#xff1a; 其实这个问题不难&#xff0c;关键…

wordpress子比主题打开文章详情页一直出现在首页的问题

遇到过几次这种情况了&#xff0c;不知是不是中了木马&#xff0c;无从下手&#xff0c;试了很多方法都不行快要疯了&#xff0c;之前试过解决不了只能重新安装&#xff0c;现在又出现了&#xff0c;第二次了&#xff0c;太麻烦了&#xff0c;突然无意中打开index.php文件发现被…

Android与RN远程过程调用的原理

Android与RN远程过程调用的原理是通过通信协议进行远程过程调用。RPC(Remote Procedure Call)是分布式系统常见的一种通信方式&#xff0c;从跨进程到跨物理机已经有几十年历史。 在React Native中&#xff0c;通信机制是一个C实现的桥&#xff0c;打通了Java和JS,实现了两者的…

数据被halo勒索病毒锁定?这里有恢复秘笈!

在当今数字化的社会&#xff0c;数据的重要性不言而喻。然而&#xff0c;随着网络技术的发展&#xff0c;数据安全问题也日益凸显。近年来&#xff0c;勒索病毒成为网络安全的一大威胁&#xff0c;其中halo勒索病毒更是让人闻风丧胆。一旦数据被这种病毒锁定&#xff0c;用户将…

42. 接雨水(Java)

目录 题目描述:输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 输入&#xff1a; height [0,1,0,2,1,0,1,3,2,1,2,1]输出&#xff1…

SpringCloud学习(11)-SpringCloudAlibaba-Nacos数据模型

一、Nacos数据模型 1.1、数据模型 对于Nacos配置管理&#xff0c;通过Namespace、Group、Date ID能够定位到一个配置集。Nacos数据模型如下所示&#xff1a; 1.2、命名空间(Namespace) 可用于进行不同环境的配置隔离。例如&#xff1a; 1)、可以隔离开发环境——测试环境和…

数据可视化高级技术Echarts(快速上手柱状图进阶操作)

目录 1.Echarts的配置 2.程序的编码 3.柱状图的实现&#xff08;入门实现&#xff09; 相关属性介绍&#xff08;进阶&#xff09;&#xff1a; 1.标记最大值/最小值 2.标记平均值 3.柱的宽度 4. 横向柱状图 5.colorBy series系列&#xff08;需要构造多组数据才能实现…

基于java实现的二手车交易网站

开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea…

C++初级----string类(STL)

1、标准库中的string 1.1、sring介绍 字符串是表示字符序列的类&#xff0c;标准的字符串类提供了对此类对象的支&#xff0c;其接口类似于标准字符容器的接口&#xff0c;但是添加了专门用于操作的单字节字符字符串的设计特性。 string类是使用char&#xff0c;即作为他的字符…

蓝桥杯 【日期统计】【01串的熵】

日期统计 第一遍写的时候会错了题目的意思&#xff0c;我以为是一定要八个整数连在一起构成正确日期&#xff0c;后面发现逻辑明明没有问题但是答案怎么都是错的才发现理解错了题目的意思&#xff0c;题目的意思是按下标顺序组成&#xff0c;意思就是可以不连续&#xff0c;我…

vue快速入门(八)绑定方法

注释很详细&#xff0c;直接上代码 上一篇 新增内容 v-if与button响应回顾事件方法写法 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, …

《QT实用小工具·十七》密钥生成工具

1、概述 源码放在文章末尾 该项目主要用于生成密钥&#xff0c;下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #pragma execution_character_set("utf-8")#include "frmmain.h" #include "ui_frmmain.h" #include "qmessag…

Spring Boot 学习(1)——环境搭建

一只老辣鸟的自我救赎 不科普&#xff0c;简单记录学习过程。 开发环境约束&#xff1a; jdk1.8 Spring Boot 1.5.9 Spring 4.3.13 Maven 3.3.3 Intellij IDEA 2017 【脑瓜灵光的开发环境随意&#xff0c;不灵光尽量按上述约束设置。看了好些教程总…

使用vue开发的前后台框架推荐

对于Vue后台前台框架&#xff0c;以下是几个值得推荐的选项&#xff1a; Element UI&#xff1a;一个基于Vue.js的桌面端组件库&#xff0c;提供了丰富的UI组件和交互方式&#xff0c;非常适合构建后台管理系统。 Element UI是一套为开发者、设计师和产品经理准备的基于Vue 2.…

蓝桥杯第十三届省赛C++B组(未完)

目录 刷题统计 修剪灌木 X进制减法 【前缀和双指针】统计子矩阵 【DP】积木画 【图DFS】扫雷 李白打酒加强版 DFS (通过64%&#xff0c;ACwing 3/11&#xff09;; DFS(AC) DP&#xff08;AC&#xff09; 砍竹子(X) 刷题统计 题目描述 小明决定从下周一开始努力刷题准…