【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;// 验证码图片Bufferprivate 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;// 图像bufferbuffImg = 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";@Resourceprivate UserInfoService userInfoService;@Resourceprivate 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层
	@Overridepublic 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);}
	@Overridepublic 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.hqwc.cn/news/597823.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode热题100.跳跃游戏2

Problem: 45. 跳跃游戏 II 文章目录 题目思路复杂度Code 题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: …

Hadoop MapReduce

MapReduce分为两个阶段&#xff0c;分为Map阶段和Reduce阶段&#xff0c;可以自定义map函数和reduce函数&#xff0c; map函数的输入是行在文件的字节偏移量&#xff0c;value是文件的一行数据。 reduce函数的输入是key和对应key的value组&#xff0c;然后reduce函数可以对这…

FebHost:墨西哥外贸独立站为何钟情于.MX域名?

在数字化时代的大潮中&#xff0c;墨西哥的互联网用户对于 .mx 域名展现出了浓厚的兴趣。这一现象背后的原因引发了广泛的关注。 首先&#xff0c;本地相关性无疑是 .mx 域名吸引墨西哥用户的重要因素。当用户在网络世界中漫游时&#xff0c;一个带有 .mx 域名的网站往往能迅速…

DSO9254A安捷伦DSO9254A示波器

181/2461/8938产品概述&#xff1a; 安捷伦DSO9254A的带宽为2.5 GHz&#xff0c;配备15英寸XGA液晶显示屏&#xff0c;采用静音封装&#xff0c;厚度仅为9英寸&#xff08;23厘米&#xff09;&#xff0c;重量仅为26磅&#xff08;11.8千克&#xff09;。DSO9254A集成了一个功…

MongoDB初探:安装与图形化界面保姆级使用指南

文章目录 前言一、MongoDB下载安装下载解压配置环境变量打开mongoDB 二、配置本地MongoDB服务创建文件下载服务测试服务 三、图形化界面Compass GUINavicat GUI 总结 前言 MongoDB是一种流行的开源、面向文档的NoSQL数据库程序。与传统的关系型数据库不同&#xff0c;MongoDB将…

观《你想活出怎样的人生》有感

《你想活出怎样的人生》 四月六号&#xff0c;和赵茜小美女观看了宫崎骏导演拍的《你想活出怎样的人生》&#xff0c;感受颇丰&#xff0c;特此写一篇文章以记之。 电影简介 《你想活出怎样的人生》是宫崎骏执导的动画电影&#xff0c;不仅是宫崎骏的复出之作&#xff0c;也…

【LeetCode热题100】74. 搜索二维矩阵(二分)

一.题目要求 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;…

【第六篇】使用BurpSuite重发请求

在研究目标网站对不同输入的响应时&#xff0c;不必每次都拦截请求&#xff0c;否则造成时间浪费&#xff0c;我们可使用 Burp Repeater 模块实现重发。 如图&#xff0c;我们可以选择某个请求包&#xff0c;单击右键&#xff0c;将其发送至Repeater模块&#xff1a; 接着&…

day03-Docker

1.初识 Docker 1.1.什么是 Docker 1.1.1.应用部署的环境问题 大型项目组件较多&#xff0c;运行环境也较为复杂&#xff0c;部署时会碰到一些问题&#xff1a; 依赖关系复杂&#xff0c;容易出现兼容性问题开发、测试、生产环境有差异 例如一个项目中&#xff0c;部署时需要依…

基于51单片机电子钟万年历设计—LCD1602显示

基于51单片机电子钟万年历设计 &#xff08;仿真&#xff0b;程序&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.DS1302记录时间&#xff1b; 2.LCD1602实时显示日期、星期、时分秒&#xff1b; 3.按键设置日期、时间、闹钟&#xff1b; 4.使用蜂鸣器…

【线段树】1622. 奇妙序列

本文涉及知识点 设计 数学 线段树 本文基础解法 【设计】 【数学】1622 奇妙序列 LeetCode1622. 奇妙序列 请你实现三个 API append&#xff0c;addAll 和 multAll 来实现奇妙序列。 请实现 Fancy 类 &#xff1a; Fancy() 初始化一个空序列对象。 void append(val) 将整数…

Ubuntu下TexStudio如何兼容中文

怎么就想起来研究一下这个&#xff1f; 我使用大名鼎鼎的3Blue1Brown数学动画引擎Manim&#xff0c;制作了一个特别小的动画视频克里金插值。在视频中&#xff0c;绘制文字时&#xff0c;Manim使用到了texlive texlive-latex-extra这些库。专业的关系&#xff0c;当年的毕设没…