springboot簡(jiǎn)單實(shí)現(xiàn)郵件功能

簡(jiǎn)單實(shí)現(xiàn)發(fā)送郵件的小功能

1.準(zhǔn)備

需要一個(gè)郵箱,并開啟POP3/IMAP/SMTP服務(wù)。
以QQ郵箱為例,郵箱設(shè)置-賬戶,就能開啟對(duì)應(yīng)的服務(wù),并獲取授權(quán)碼。


開啟服務(wù),獲取郵件授權(quán)
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配置文件中,配置相應(yīng)的參數(shù)
spring:
  mail:
    host: smtp.qq.com #QQ的發(fā)送郵件服務(wù)器
    username: 郵箱
    password: 授權(quán)碼
4.編寫一個(gè)工具類
@Data
@Slf4j
@Component
public class MailUtil {

    //注入郵件服務(wù)
    @Autowired
    private JavaMailSenderImpl mailSender;
    //誰來發(fā)
    @Value("${spring.mail.username}")
    private String from;

    /**
     * 發(fā)送簡(jiǎn)單郵件
     * @param email 發(fā)給誰
     * @param subject 郵件主題
     * @param text 郵件內(nèi)容
     */
    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("發(fā)送失??!",e);
            throw new RuntimeException(e);
        }
    }
}
5.編寫業(yè)務(wù)方法,以發(fā)送驗(yàn)證碼為例
@Async("myExecutor") 
public void sendCodeMail(String email) {
    //六位隨機(jī)數(shù)的驗(yàn)證碼
    String code = CodeUtil.randomCode(6);
    //郵件主題
    String subject = "驗(yàn)證碼";
    //郵件內(nèi)容
    String text = "驗(yàn)證碼:" + code +",10分鐘之內(nèi)有效。";

    Map<String,Object> map = new HashMap<>(2);
    map.put("code",code);
    map.put("createTime",System.currentTimeMillis());

    //將驗(yàn)證碼緩存進(jìn)redis中,并設(shè)置過期時(shí)間,方便后續(xù)做校驗(yàn)
    redisTemplate.opsForHash().putAll(ConstantUtil.USER_MAIL_CODE + email,map);
    redisTemplate.expire(ConstantUtil.USER_MAIL_CODE + email,10,TimeUnit.MINUTES);

    mailUtil.sendSimpleMail(email,subject,text);
}

/**
 * 說明:由于發(fā)送郵件,可能會(huì)比較耗時(shí)間,所以需要異步調(diào)用。
 * 這里的處理方式是,配置一個(gè)線程池,通過@Async("myExecutor"),另啟線程調(diào)用。
 * 偷懶的話,可以在啟動(dòng)類添加注解@EnableAsync,再在方法上用注解@Async,也可以開啟異步調(diào)用。
 */
 
/**
 * 限制一分鐘內(nèi)只能發(fā)一條,避免惡意攻擊
 */
public boolean checkSendToLegal(String email) {
        //利用正則表達(dá)式,驗(yàn)證郵箱是否符合郵箱的格式
        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.編寫測(cè)試方法
@Test
public void sendCodeMail(){
    //發(fā)給誰
    String email = "";
    //限制一分鐘內(nèi)只能發(fā)一條,避免惡意攻擊
    if(checkSendToLegal(email)){
        log.error("操作過于頻繁,請(qǐng)一分鐘后再試!");
    }
    //發(fā)送
    sendCodeMail(email);
}

效果截圖:
[圖片上傳失敗...(image-20a105-1634463311376)]

7.發(fā)送復(fù)雜郵件,以給郵箱發(fā)送激活鏈接為例
  • 在工具類中添加如下方法
/**
 * 發(fā)送定制郵件
 * @param template 模板
 */
public void sendMimeMail(MailTemplate template){
  try {
      //true表示支持復(fù)雜類型
      MimeMessageHelper messageHelper = new MimeMessageHelper(mailSender.createMimeMessage(), true);
      //郵件發(fā)信人
      messageHelper.setFrom(from);
      //郵件收信人
      if (!ObjectUtils.isEmpty(template.getTo())) {
          messageHelper.setTo(template.getTo());
      }
      //給誰回復(fù)
      if(StringUtils.hasText(template.getReplyTo())){
          messageHelper.setReplyTo(template.getReplyTo());
      }
      //郵件主題
      if (StringUtils.hasText(template.getSubject())) {
          messageHelper.setSubject(template.getSubject());
      }
      //郵件內(nèi)容,true時(shí),顯示html代碼效果,默認(rèn)為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);
          }
      }
      //發(fā)送時(shí)間
      if (!ObjectUtils.isEmpty(template.getSentDate())) {
          template.setSentDate(new Date());
          messageHelper.setSentDate(template.getSentDate());
      }
      //正式發(fā)送郵件
      mailSender.send(messageHelper.getMimeMessage());
      template.setStatus("1");
  } catch (Exception e) {
      log.error("發(fā)送失敗!",e);
      throw new RuntimeException(e);
  }
}

/**
 * 郵件模板
 */
@Data
class MailTemplate {

    //發(fā)送人
    private String from;
    //接受人
    private String[] to;
    //回復(fù)給誰
    private String replyTo;
    //抄送
    private String[] cc;
    //密送
    private String[] bcc;
    //發(fā)送時(shí)間
    private Date sentDate;
    //主題
    private String subject;
    //郵件內(nèi)容
    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;
    }
}

  • 添加一個(gè)發(fā)送郵件的業(yè)務(wù)方法
@Async("myExecutor")
public void sendLinkMail(String email) {
    //用email生產(chǎn)token串
    String token = JWTUtil.createToken(email);
    String subject = "賬戶激活";
    String text = "<!DOCTYPE html><html><head></head>" +
            "<body>" +
            "<h1>點(diǎn)擊下面的按鈕即可激活賬戶</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);
}

/**
 * 根據(jù)token獲取email信息,校驗(yàn)緩存中是否存在
 */
public boolean verifyLink(String token) {
    String email = JWTUtil.getUsernameByToken(token);
    Object o = redisTemplate.opsForValue().get(ConstantUtil.USER_MAIL_LINK + email);
    return ObjectUtils.isEmpty(o);
}
/**
 * 簡(jiǎn)單的token生產(chǎn)工具
 */
public class JWTUtil {

    private static String key = "tokenKey";

    /**
     * 根據(jù)用戶名生成token
     * @param username
     * @return
     */
    public static String createToken(String username){
        return Jwts.builder().setSubject(username).signWith(SignatureAlgorithm.HS512, key).compressWith(CompressionCodecs.GZIP).compact();
    }

    /**
     * 根據(jù)token 獲取用戶名
     * @param token
     * @return
     */
    public static String getUsernameByToken(String token){
        return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().getSubject();
    }
}
  • 添加相關(guān)的模擬接口
/**
 * 模擬 給郵箱發(fā)送激活鏈接
 * @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("激活失敗,認(rèn)證信息已經(jīng)失效!");
    }
    // todo 激活賬戶相關(guān)業(yè)務(wù)

    // todo 最后,刪除對(duì)應(yīng)的緩存
    
    return R.ok("success");
}

效果截圖:


激活鏈接
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容