Skip to content

Commit

Permalink
feat: 邮箱验证码验证 (#14)
Browse files Browse the repository at this point in the history
* feat: add a status code

* EmailIdentifyingException.java

feat: add a definition of exception

* feat: add a definition of exception

* feat: add a tool about email check

* feat: add a email check service

* feat: add a Reposiroty class to exchange with Redis

* feat: definition entity of model html

* feat: add email model html

* feat: add two api

* feat: add a default mail against error

* feat: add a annotation

* feat: add a validator

* feat: add a dependencies

* fix: 修复 validate 使用上的一些问题

* feat: add a definition of exception

* feat: add predicate of email pattern

* feat: add some status code

* feat: close a worthless log property

* feat: change some properties

* fix: adjust some code

* fix: 调整代码位置,更改代码逻辑,还有点待做明天搞

* fix: delete some unrelated code

* fix: modify the definition of status codes

* fix: delete some blank link

* fix: adjust the code to separate RedisCache

* fix: delete some sensitive properties

* feat: add some methods

* feat: change to a detail name

* extract email verification service

* fix: adjust some codes

* fix: fix a bug

* feat: add a check opportunities for users

* fix: code format and location

* fix: code reformat

* feat: delete a file

* fix: add timeout

* improve email exception handling

* fix: adjust some codes

* fix: adjust some codes

* feat: add a global exception

* fix: remove a file

* fix: adjust the code about handling exception

* feat: check email pattern because the annotation is invalid

* fix: adjust some code

* feat: add some commom method

* fix: adjust som codes

* fix: fix a bug

* fix: adjust some codes

* feat: move two files

* feat: add configuration to prevent errors

* fix: remove two files

* fix: adjust  the overall logic of the code

* fix: rename some field and files

* feat: add some method

* fix: move some files

* fix: remove some files

* fix:

* feat: add a todo mask

---------

Co-authored-by: s:103 <mamingsheng103@yeah.net>
Co-authored-by: BanTanger <1290288968@qq.com>
  • Loading branch information
3 people authored Jan 19, 2024
1 parent b5d068e commit e389bf8
Show file tree
Hide file tree
Showing 44 changed files with 859 additions and 329 deletions.
14 changes: 11 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
Expand Down Expand Up @@ -118,19 +123,22 @@
<version>5.8.11</version>
</dependency>

<!-- 表格生成 -->
<!-- 表格生成 -->
<!--easypoi-->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>

</dependencies>



<build>
<plugins>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public enum GlobalServiceStatusCode {
PARAM_IS_BLANK(1002, "参数为空"),
PARAM_TYPE_ERROR(1003, "参数类型错误"),
PARAM_NOT_COMPLETE(1004, "参数缺失"),
PARAM_FAILED_VALIDATE(1005, "参数未通过验证"),

/* 用户错误 2001-3000 */
USER_NOT_LOGIN(2001, "用户未登录"),
Expand All @@ -42,6 +43,13 @@ public enum GlobalServiceStatusCode {
USER_NO_PERMISSION(2403, "用户无权限"),
USER_NO_PHONE_CODE(2500, "验证码错误"),

/* 邮箱错误 3001-4000 */
EMAIL_PATTERN_ERROR(3001, "邮箱格式错误"),
EMAIL_SEND_FAIL(3002, "邮箱发送失败"),

EMAIL_NOT_EXIST_RECORD(3101, "邮箱不存在记录"),
EMAIL_CODE_NOT_CONSISTENT(3102, "邮箱验证码不一致"),
EMAIL_CODE_OPPORTUNITIES_EXHAUST(3103, "验证次数达到上限"),

/* -------------- */;

Expand All @@ -53,7 +61,6 @@ public enum GlobalServiceStatusCode {
this.message = message;
}


/**
* 根据code获取message
*
Expand Down
103 changes: 51 additions & 52 deletions src/main/java/com/achobeta/domain/email/component/EmailSender.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.achobeta.domain.email.component;

import cn.hutool.core.bean.BeanUtil;
import com.achobeta.domain.email.component.po.Email;
import com.achobeta.exception.ParameterValidateException;
import com.achobeta.exception.SendMailException;
import com.achobeta.common.constants.GlobalServiceStatusCode;
import com.achobeta.domain.email.component.po.EmailMessage;
import com.achobeta.exception.GlobalServiceException;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
Expand All @@ -20,79 +20,77 @@
import java.util.Objects;
import java.util.function.Function;


@Component
@Slf4j
@RequiredArgsConstructor
public class EmailSender {

private final JavaMailSender javaMailSender;

private final TemplateEngine templateEngine;

public SimpleMailMessage emailToSimpleMailMessage(Email email) {
public SimpleMailMessage emailToSimpleMailMessage(EmailMessage emailMessage) {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setFrom(email.getSender());
simpleMailMessage.setTo(email.getRecipient());
simpleMailMessage.setCc(email.getCarbonCopy());
simpleMailMessage.setSubject(email.getTitle());
simpleMailMessage.setText(email.getContent());
simpleMailMessage.setFrom(emailMessage.getSender());
simpleMailMessage.setTo(emailMessage.getRecipient());
simpleMailMessage.setCc(emailMessage.getCarbonCopy());
simpleMailMessage.setSubject(emailMessage.getTitle());
simpleMailMessage.setText(emailMessage.getContent());
return simpleMailMessage;
}


public MimeMessageHelper emailIntoMimeMessageByHelper(MimeMessage mimeMessage, Email email) {
public MimeMessageHelper emailIntoMimeMessageByHelper(MimeMessage mimeMessage, EmailMessage emailMessage) {
try {
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom(email.getSender());
mimeMessageHelper.setCc(email.getCarbonCopy());
mimeMessageHelper.setSubject(email.getTitle());
mimeMessageHelper.setTo(email.getRecipient());
mimeMessageHelper.setFrom(emailMessage.getSender());
mimeMessageHelper.setCc(emailMessage.getCarbonCopy());
mimeMessageHelper.setSubject(emailMessage.getTitle());
mimeMessageHelper.setTo(emailMessage.getRecipient());
return mimeMessageHelper;
} catch (MessagingException e) {
throw new SendMailException(e.getMessage());
throw new GlobalServiceException(e.getMessage(), GlobalServiceStatusCode.EMAIL_SEND_FAIL);
}
}

public void sendSimpleMailMessage(Email email) {
if(Objects.isNull(email)) {
throw new ParameterValidateException("email不能为空");
public void sendSimpleMailMessage(EmailMessage emailMessage) {
if (Objects.isNull(emailMessage)) {
throw new GlobalServiceException("email不能为空", GlobalServiceStatusCode.PARAM_IS_BLANK);
}
// 封装simpleMailMessage对象
SimpleMailMessage simpleMailMessage = emailToSimpleMailMessage(email);
SimpleMailMessage simpleMailMessage = emailToSimpleMailMessage(emailMessage);
// 发送
javaMailSender.send(simpleMailMessage);
}


public void sendMailWithFile(Email email, File... files) {
if(Objects.isNull(email)) {
throw new ParameterValidateException("email不能为空");
public void sendMailWithFile(EmailMessage emailMessage, File... files) {
if (Objects.isNull(emailMessage)) {
throw new GlobalServiceException("email不能为空", GlobalServiceStatusCode.PARAM_IS_BLANK);
}
// 封装对象
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = emailIntoMimeMessageByHelper(mimeMessage, email);
MimeMessageHelper mimeMessageHelper = emailIntoMimeMessageByHelper(mimeMessage, emailMessage);
// 添加附件
for(File file : files) {
if(Objects.nonNull(file)) {
for (File file : files) {
if (Objects.nonNull(file)) {
mimeMessageHelper.addAttachment(file.getName(), file);
}
}
mimeMessageHelper.setText(email.getContent(), false);
mimeMessageHelper.setText(emailMessage.getContent(), false);
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
throw new SendMailException(e.getMessage());
throw new GlobalServiceException(e.getMessage(), GlobalServiceStatusCode.EMAIL_SEND_FAIL);
}
}

public void sendModelMail(Email email, String template, Object modelMessage) {
if(Objects.isNull(email)) {
throw new ParameterValidateException("email不能为空");
public void sendModelMail(EmailMessage emailMessage, String template, Object modelMessage) {
if (Objects.isNull(emailMessage)) {
throw new GlobalServiceException("email不能为空", GlobalServiceStatusCode.PARAM_IS_BLANK);
}
// 封装对象
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = emailIntoMimeMessageByHelper(mimeMessage, email);
MimeMessageHelper mimeMessageHelper = emailIntoMimeMessageByHelper(mimeMessage, emailMessage);
// 构造模板消息
Context context = new Context();
context.setVariables(BeanUtil.beanToMap(modelMessage));
Expand All @@ -101,43 +99,44 @@ public void sendModelMail(Email email, String template, Object modelMessage) {
mimeMessageHelper.setText(content, true);
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
throw new SendMailException(e.getMessage());
throw new GlobalServiceException(e.getMessage(), GlobalServiceStatusCode.EMAIL_SEND_FAIL);
}
}
public void sendModelMailWithFile(Email email, String template, Object modelMessage, File... files) {
if(Objects.isNull(email)) {
throw new ParameterValidateException("email不能为空");

public void sendModelMailWithFile(EmailMessage emailMessage, String template, Object modelMessage, File... files) {
if (Objects.isNull(emailMessage)) {
throw new GlobalServiceException("email不能为空", GlobalServiceStatusCode.PARAM_IS_BLANK);
}
// 封装对象
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = emailIntoMimeMessageByHelper(mimeMessage, email);
MimeMessageHelper mimeMessageHelper = emailIntoMimeMessageByHelper(mimeMessage, emailMessage);
// 构造模板消息
Context context = new Context();
context.setVariables(BeanUtil.beanToMap(modelMessage));
//合并模板与数据
String content = templateEngine.process(template, context);
mimeMessageHelper.setText(content, true);
// 添加附件
for(File file : files) {
if(Objects.nonNull(file)) {
for (File file : files) {
if (Objects.nonNull(file)) {
mimeMessageHelper.addAttachment(file.getName(), file);
}
}
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
throw new SendMailException(e.getMessage());
throw new GlobalServiceException(e.getMessage(), GlobalServiceStatusCode.EMAIL_SEND_FAIL);
}
}

public <T, R> void customizedSendEmail(Email email, String template, Function<T, R> function, File... files) {
if(Objects.isNull(email)) {
throw new ParameterValidateException("email不能为空");
public <T, R> void customizedSendEmail(EmailMessage emailMessage, String template, Function<T, R> function, File... files) {
if (Objects.isNull(emailMessage)) {
throw new GlobalServiceException("email不能为空", GlobalServiceStatusCode.PARAM_IS_BLANK);
}
String sender = email.getSender();
String[] carbonCopy = email.getCarbonCopy();
String title = email.getTitle();
Arrays.stream(email.getRecipient())
String sender = emailMessage.getSender();
String[] carbonCopy = emailMessage.getCarbonCopy();
String title = emailMessage.getTitle();
Arrays.stream(emailMessage.getRecipient())
.parallel()
.distinct()
.forEach(s -> {
Expand All @@ -150,8 +149,8 @@ public <T, R> void customizedSendEmail(Email email, String template, Function<T,
mimeMessageHelper.setCc(carbonCopy);
mimeMessageHelper.setSubject(title);
// 添加附件
for(File file : files) {
if(Objects.nonNull(file)) {
for (File file : files) {
if (Objects.nonNull(file)) {
mimeMessageHelper.addAttachment(file.getName(), file);
}
}
Expand All @@ -166,7 +165,7 @@ public <T, R> void customizedSendEmail(Email email, String template, Function<T,
//发送
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
throw new SendMailException(e.getMessage());
throw new GlobalServiceException(e.getMessage(), GlobalServiceStatusCode.EMAIL_SEND_FAIL);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.achobeta.domain.email.component;

public class EmailValidator {

public static final String EMAIL_PATTERN = "^[\\w\\.-]+@[a-zA-Z\\d\\.-]+\\.[a-zA-Z]{2,}$";

public static boolean isEmailAccessible(String email) {
return email.matches(EMAIL_PATTERN);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* 封装这个对象,传给EmailSender就可以了
*/
@Data
public class Email implements Serializable {
public class EmailMessage implements Serializable {


private String sender;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.achobeta.domain.email.controller;

import com.achobeta.common.SystemJsonResponse;
import com.achobeta.domain.email.service.EmailService;
import com.achobeta.domain.email.util.IdentifyingCodeValidator;
import jakarta.validation.constraints.Email;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
@RequiredArgsConstructor
@Validated
@RequestMapping("/api/v1/email")
public class EmailController {

private final EmailService emailService;

/**
* 发送验证码接口
*
* @param email
* @return
*/
@PostMapping("/check")
public SystemJsonResponse emailIdentityCheck(@RequestParam("email") @Email String email) {
// 获得随机验证码
String code = IdentifyingCodeValidator.getIdentifyingCode();
emailService.sendIdentifyingCode(email, code);
// 能到这一步就成功了
log.info("发送验证码:{} -> email:{}", code, email);
return SystemJsonResponse.SYSTEM_SUCCESS();
}

@PostMapping("/check/{code}")
public SystemJsonResponse checkCode(@RequestParam("email") @Email String email,
@PathVariable("code") @NonNull String code) {
// 验证
emailService.checkIdentifyingCode(email, code);
// 成功
log.info("email:{}, 验证码:{} 验证成功", email, code);
return SystemJsonResponse.SYSTEM_SUCCESS();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.achobeta.domain.email.model.vo;

import lombok.Builder;
import lombok.Getter;

/**
* 生命周期只有一次,成员变量对修改关闭,直接通过 builder 构建对象
*/
@Getter
@Builder
public class VerificationCodeTemplate {

private String code; // 验证码

private int minutes; // 过期时间分钟数

}
Loading

0 comments on commit e389bf8

Please sign in to comment.