新增:新增修改邮箱功能,并优化部分以往代码(引入 spring-boot-starter-mail 用于发送邮件验证码)

This commit is contained in:
2023-01-14 01:05:39 +08:00
parent 73fadb8315
commit 8b82557883
45 changed files with 1318 additions and 280 deletions

View File

@@ -1,72 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.charles7c.cnadmin.webapi.controller.auth;
import java.time.Duration;
import lombok.RequiredArgsConstructor;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wf.captcha.base.Captcha;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.IdUtil;
import top.charles7c.cnadmin.auth.config.properties.CaptchaProperties;
import top.charles7c.cnadmin.auth.model.vo.CaptchaVO;
import top.charles7c.cnadmin.common.model.vo.R;
import top.charles7c.cnadmin.common.util.RedisUtils;
/**
* 验证码 API
*
* @author Charles7c
* @since 2022/12/11 14:00
*/
@Tag(name = "验证码 API")
@SaIgnore
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/captcha", produces = MediaType.APPLICATION_JSON_VALUE)
public class CaptchaController {
private final CaptchaProperties captchaProperties;
@Operation(summary = "获取图片验证码", description = "获取图片验证码Base64编码带图片格式data:image/gif;base64")
@GetMapping("/img")
public R<CaptchaVO> getImageCaptcha() {
// 生成验证码
Captcha captcha = captchaProperties.getCaptcha();
// 保存验证码
String uuid = IdUtil.fastSimpleUUID();
String captchaKey = RedisUtils.formatKey(captchaProperties.getKeyPrefix(), uuid);
RedisUtils.setCacheObject(captchaKey, captcha.text(),
Duration.ofMinutes(captchaProperties.getExpirationInMinutes()));
// 返回验证码
CaptchaVO captchaVo = new CaptchaVO().setUuid(uuid).setImg(captcha.toBase64());
return R.ok(captchaVo);
}
}

View File

@@ -31,11 +31,12 @@ import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import top.charles7c.cnadmin.auth.config.properties.CaptchaProperties;
import top.charles7c.cnadmin.auth.model.request.LoginRequest;
import top.charles7c.cnadmin.auth.model.vo.LoginVO;
import top.charles7c.cnadmin.auth.model.vo.UserInfoVO;
import top.charles7c.cnadmin.auth.service.LoginService;
import top.charles7c.cnadmin.common.config.properties.CaptchaProperties;
import top.charles7c.cnadmin.common.consts.CacheConstants;
import top.charles7c.cnadmin.common.model.dto.LoginUser;
import top.charles7c.cnadmin.common.model.vo.R;
import top.charles7c.cnadmin.common.util.ExceptionUtils;
@@ -64,11 +65,11 @@ public class LoginController {
@PostMapping("/login")
public R<LoginVO> login(@Validated @RequestBody LoginRequest loginRequest) {
// 校验验证码
String captchaKey = RedisUtils.formatKey(captchaProperties.getKeyPrefix(), loginRequest.getUuid());
String captchaKey = RedisUtils.formatKey(CacheConstants.CAPTCHA_CACHE_KEY, loginRequest.getUuid());
String captcha = RedisUtils.getCacheObject(captchaKey);
ValidationUtils.exIfBlank(captcha, "验证码已失效");
RedisUtils.deleteCacheObject(captchaKey);
ValidationUtils.exIfCondition(() -> !captcha.equalsIgnoreCase(loginRequest.getCaptcha()), "验证码错误");
ValidationUtils.exIfNotEqualIgnoreCase(loginRequest.getCaptcha(), captcha, "验证码错误");
// 用户登录
String rawPassword =
@@ -84,7 +85,6 @@ public class LoginController {
in = ParameterIn.HEADER)
@PostMapping("/logout")
public R logout() {
ValidationUtils.exIfCondition(() -> !StpUtil.isLogin(), "Token 无效");
StpUtil.logout();
return R.ok();
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.charles7c.cnadmin.webapi.controller.common;
import java.time.Duration;
import javax.mail.MessagingException;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import lombok.RequiredArgsConstructor;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wf.captcha.base.Captcha;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import top.charles7c.cnadmin.common.config.properties.CaptchaProperties;
import top.charles7c.cnadmin.common.config.properties.ContinewAdminProperties;
import top.charles7c.cnadmin.common.consts.CacheConstants;
import top.charles7c.cnadmin.common.model.vo.CaptchaVO;
import top.charles7c.cnadmin.common.model.vo.R;
import top.charles7c.cnadmin.common.util.*;
import top.charles7c.cnadmin.common.util.validate.ValidationUtils;
/**
* 验证码 API
*
* @author Charles7c
* @since 2022/12/11 14:00
*/
@Tag(name = "验证码 API")
@SaIgnore
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/common/captcha", produces = MediaType.APPLICATION_JSON_VALUE)
public class CaptchaController {
private final CaptchaProperties captchaProperties;
private final ContinewAdminProperties properties;
@Operation(summary = "获取图片验证码", description = "获取图片验证码Base64编码带图片格式data:image/gif;base64")
@GetMapping("/img")
public R<CaptchaVO> getImageCaptcha() {
// 生成验证码
CaptchaProperties.CaptchaImage captchaImage = captchaProperties.getImage();
Captcha captcha = captchaImage.getCaptcha();
// 保存验证码
String uuid = IdUtil.fastSimpleUUID();
String captchaKey = RedisUtils.formatKey(CacheConstants.CAPTCHA_CACHE_KEY, uuid);
RedisUtils.setCacheObject(captchaKey, captcha.text(),
Duration.ofMinutes(captchaImage.getExpirationInMinutes()));
// 返回验证码
CaptchaVO captchaVo = new CaptchaVO().setUuid(uuid).setImg(captcha.toBase64());
return R.ok(captchaVo);
}
@Operation(summary = "获取邮箱验证码", description = "发送验证码到指定邮箱")
@GetMapping("/mail")
public R getMailCaptcha(
@NotBlank(message = "邮箱不能为空") @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") String email)
throws MessagingException {
// 校验
String limitCacheKey = CacheConstants.LIMIT_CACHE_KEY;
String captchaCacheKey = CacheConstants.CAPTCHA_CACHE_KEY;
String limitCaptchaKey = RedisUtils.formatKey(limitCacheKey, captchaCacheKey, email);
long limitTimeInMillisecond = RedisUtils.getTimeToLive(limitCaptchaKey);
ValidationUtils.exIfCondition(() -> limitTimeInMillisecond > 0,
String.format("发送邮箱验证码过于频繁,请您 %ds 后再试", limitTimeInMillisecond / 1000));
// 生成验证码
CaptchaProperties.CaptchaMail captchaMail = captchaProperties.getMail();
String captcha = RandomUtil.randomNumbers(captchaMail.getLength());
// 发送验证码
Long expirationInMinutes = captchaMail.getExpirationInMinutes();
String content = TemplateUtils.render(captchaMail.getTemplatePath(),
Dict.create().set("captcha", captcha).set("expiration", expirationInMinutes));
MailUtils.sendHtml(email, String.format("【%s】邮箱验证码", properties.getName()), content);
// 保存验证码
String captchaKey = RedisUtils.formatKey(CacheConstants.CAPTCHA_CACHE_KEY, email);
RedisUtils.setCacheObject(captchaKey, captcha, Duration.ofMinutes(expirationInMinutes));
RedisUtils.setCacheObject(limitCaptchaKey, captcha, Duration.ofSeconds(captchaMail.getLimitInSeconds()));
return R.ok(String.format("发送成功,验证码有效期 %s 分钟", expirationInMinutes));
}
}

View File

@@ -34,15 +34,18 @@ import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import top.charles7c.cnadmin.common.config.properties.LocalStorageProperties;
import top.charles7c.cnadmin.common.consts.CacheConstants;
import top.charles7c.cnadmin.common.consts.FileConstants;
import top.charles7c.cnadmin.common.consts.RegExpConstants;
import top.charles7c.cnadmin.common.model.vo.R;
import top.charles7c.cnadmin.common.util.ExceptionUtils;
import top.charles7c.cnadmin.common.util.RedisUtils;
import top.charles7c.cnadmin.common.util.SecureUtils;
import top.charles7c.cnadmin.common.util.helper.LoginHelper;
import top.charles7c.cnadmin.common.util.validate.ValidationUtils;
import top.charles7c.cnadmin.system.model.entity.SysUser;
import top.charles7c.cnadmin.system.model.request.UpdateBasicInfoRequest;
import top.charles7c.cnadmin.system.model.request.UpdateEmailRequest;
import top.charles7c.cnadmin.system.model.request.UpdatePasswordRequest;
import top.charles7c.cnadmin.system.model.vo.AvatarVO;
import top.charles7c.cnadmin.system.service.UserService;
@@ -111,4 +114,24 @@ public class UserCenterController {
userService.updatePassword(rawOldPassword, rawNewPassword, LoginHelper.getUserId());
return R.ok("修改成功");
}
@Operation(summary = "修改邮箱", description = "修改用户邮箱")
@PatchMapping("/email")
public R updateEmail(@Validated @RequestBody UpdateEmailRequest updateEmailRequest) {
// 解密
String rawCurrentPassword =
ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateEmailRequest.getCurrentPassword()));
ValidationUtils.exIfBlank(rawCurrentPassword, "当前密码解密失败");
// 校验
String captchaKey = RedisUtils.formatKey(CacheConstants.CAPTCHA_CACHE_KEY, updateEmailRequest.getNewEmail());
String captcha = RedisUtils.getCacheObject(captchaKey);
ValidationUtils.exIfBlank(captcha, "验证码已失效");
ValidationUtils.exIfNotEqualIgnoreCase(updateEmailRequest.getCaptcha(), captcha, "验证码错误");
RedisUtils.deleteCacheObject(captchaKey);
// 修改邮箱
userService.updateEmail(updateEmailRequest.getNewEmail(), rawCurrentPassword, LoginHelper.getUserId());
return R.ok("修改成功");
}
}