简单实现发送邮件的小功能
1.准备
需要一个邮箱,并开启POP3/IMAP/SMTP服务。
以QQ邮箱为例,邮箱设置-账户,就能开启对应的服务,并获取授权码。
2.引入依赖
<!-- 版本由springboot管理 -->
<!-- 邮件依赖 (必要) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- redis (非必要)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- JWT (非必要) -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
3.在application.yml配置文件中,配置相应的参数
spring:
mail:
host: smtp.qq.com #QQ的发送邮件服务器
username: 邮箱
password: 授权码
4.编写一个工具类
@Data
@Slf4j
@Component
public class MailUtil {
//注入邮件服务
@Autowired
private JavaMailSenderImpl mailSender;
//谁来发
@Value("${spring.mail.username}")
private String from;
/**
* 发送简单邮件
* @param email 发给谁
* @param subject 邮件主题
* @param text 邮件内容
*/
public void sendSimpleMail(String email,String subject,String text){
try {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setFrom(from);
simpleMailMessage.setTo(email);
simpleMailMessage.setSubject(subject);
simpleMailMessage.setText(text);
mailSender.send(simpleMailMessage);
}catch (Exception e){
log.error("发送失败!",e);
throw new RuntimeException(e);
}
}
}
5.编写业务方法,以发送验证码为例
@Async("myExecutor")
public void sendCodeMail(String email) {
//六位随机数的验证码
String code = CodeUtil.randomCode(6);
//邮件主题
String subject = "验证码";
//邮件内容
String text = "验证码:" + code +",10分钟之内有效。";
Map<String,Object> map = new HashMap<>(2);
map.put("code",code);
map.put("createTime",System.currentTimeMillis());
//将验证码缓存进redis中,并设置过期时间,方便后续做校验
redisTemplate.opsForHash().putAll(ConstantUtil.USER_MAIL_CODE + email,map);
redisTemplate.expire(ConstantUtil.USER_MAIL_CODE + email,10,TimeUnit.MINUTES);
mailUtil.sendSimpleMail(email,subject,text);
}
/**
* 说明:由于发送邮件,可能会比较耗时间,所以需要异步调用。
* 这里的处理方式是,配置一个线程池,通过@Async("myExecutor"),另启线程调用。
* 偷懒的话,可以在启动类添加注解@EnableAsync,再在方法上用注解@Async,也可以开启异步调用。
*/
/**
* 限制一分钟内只能发一条,避免恶意攻击
*/
public boolean checkSendToLegal(String email) {
//利用正则表达式,验证邮箱是否符合邮箱的格式
if(!email.matches("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$")){
return true;
}
Long now = System.currentTimeMillis();
Long codeCreateTime =(Long)redisTemplate.opsForHash().get(ConstantUtil.USER_MAIL_CODE + email, "createTime");
return codeCreateTime != null && now - codeCreateTime < 60000;
}
6.编写测试方法
@Test
public void sendCodeMail(){
//发给谁
String email = "";
//限制一分钟内只能发一条,避免恶意攻击
if(checkSendToLegal(email)){
log.error("操作过于频繁,请一分钟后再试!");
}
//发送
sendCodeMail(email);
}
效果截图:
[图片上传失败...(image-20a105-1634463311376)]
7.发送复杂邮件,以给邮箱发送激活链接为例
- 在工具类中添加如下方法
/**
* 发送定制邮件
* @param template 模板
*/
public void sendMimeMail(MailTemplate template){
try {
//true表示支持复杂类型
MimeMessageHelper messageHelper = new MimeMessageHelper(mailSender.createMimeMessage(), true);
//邮件发信人
messageHelper.setFrom(from);
//邮件收信人
if (!ObjectUtils.isEmpty(template.getTo())) {
messageHelper.setTo(template.getTo());
}
//给谁回复
if(StringUtils.hasText(template.getReplyTo())){
messageHelper.setReplyTo(template.getReplyTo());
}
//邮件主题
if (StringUtils.hasText(template.getSubject())) {
messageHelper.setSubject(template.getSubject());
}
//邮件内容,true时,显示html代码效果,默认为false
if (StringUtils.hasText(template.getText())) {
messageHelper.setText(template.getText(),template.isHtml());
}
//抄送
if (!ObjectUtils.isEmpty(template.getCc())) {
messageHelper.setCc(template.getCc());
}
//密送
if (!ObjectUtils.isEmpty(template.getBcc())) {
messageHelper.setCc(template.getBcc());
}
//添加邮件附件
if (!CollectionUtils.isEmpty(template.getMultipartFiles())) {
for (MultipartFile multipartFile : template.getMultipartFiles()) {
messageHelper.addAttachment(Objects.requireNonNull(multipartFile.getOriginalFilename()), multipartFile);
}
}
//发送时间
if (!ObjectUtils.isEmpty(template.getSentDate())) {
template.setSentDate(new Date());
messageHelper.setSentDate(template.getSentDate());
}
//正式发送邮件
mailSender.send(messageHelper.getMimeMessage());
template.setStatus("1");
} catch (Exception e) {
log.error("发送失败!",e);
throw new RuntimeException(e);
}
}
/**
* 邮件模板
*/
@Data
class MailTemplate {
//发送人
private String from;
//接受人
private String[] to;
//回复给谁
private String replyTo;
//抄送
private String[] cc;
//密送
private String[] bcc;
//发送时间
private Date sentDate;
//主题
private String subject;
//邮件内容
private String text;
//邮件附件
List<MultipartFile> multipartFiles;
//是否显示html代码效果
private boolean html;
private String status;
public void setTo(String to) {
this.to = new String[]{to};
}
public void setTo(String... to) {
this.to = to;
}
public String[] getTo() {
return this.to;
}
public void setCc(String cc) {
this.cc = new String[]{cc};
}
public void setCc(String... cc) {
this.cc = cc;
}
public String[] getCc() {
return this.cc;
}
public void setBcc(String bcc) {
this.bcc = new String[]{bcc};
}
public void setBcc(String... bcc) {
this.bcc = bcc;
}
public String[] getBcc() {
return this.bcc;
}
}
- 添加一个发送邮件的业务方法
@Async("myExecutor")
public void sendLinkMail(String email) {
//用email生产token串
String token = JWTUtil.createToken(email);
String subject = "账户激活";
String text = "<!DOCTYPE html><html><head></head>" +
"<body>" +
"<h1>点击下面的按钮即可激活账户</h1>\n" +
"<h3><a href='http://localhost:8080/test/verify_email?token="+token+"'>立即激活</a></h3>" +
"</body>" +
"</html>";
MailTemplate mailTemplate = new MailTemplate();
mailTemplate.setTo(email);
mailTemplate.setSentDate(new Date());
mailTemplate.setSubject(subject);
mailTemplate.setText(text);
mailTemplate.setHtml(true);
//缓存token信息,email作为key
redisTemplate.opsForValue().set(ConstantUtil.USER_MAIL_LINK + email,token,7,TimeUnit.DAYS);
mailUtil.sendMimeMail(mailTemplate);
}
/**
* 根据token获取email信息,校验缓存中是否存在
*/
public boolean verifyLink(String token) {
String email = JWTUtil.getUsernameByToken(token);
Object o = redisTemplate.opsForValue().get(ConstantUtil.USER_MAIL_LINK + email);
return ObjectUtils.isEmpty(o);
}
/**
* 简单的token生产工具
*/
public class JWTUtil {
private static String key = "tokenKey";
/**
* 根据用户名生成token
* @param username
* @return
*/
public static String createToken(String username){
return Jwts.builder().setSubject(username).signWith(SignatureAlgorithm.HS512, key).compressWith(CompressionCodecs.GZIP).compact();
}
/**
* 根据token 获取用户名
* @param token
* @return
*/
public static String getUsernameByToken(String token){
return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().getSubject();
}
}
- 添加相关的模拟接口
/**
* 模拟 给邮箱发送激活链接
* @param email
* @return
*/
@GetMapping("sendLink")
public R sendLinkMail(@RequestParam String email){
mailService.sendLinkMail(email);
return R.ok("success");
}
/**
* 激活链接url
* @param token
* @return
*/
@GetMapping("verify_email")
public R verifyEmail(@RequestParam String token){
if(mailService.verifyLink(token)){
return R.failed("激活失败,认证信息已经失效!");
}
// todo 激活账户相关业务
// todo 最后,删除对应的缓存
return R.ok("success");
}
效果截图: