springboot簡單實現(xiàn)郵件功能

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

1.準備

需要一個郵箱躬翁,并開啟POP3/IMAP/SMTP服務(wù)蠢箩。
以QQ郵箱為例挨决,郵箱設(shè)置-賬戶印颤,就能開啟對應(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.編寫一個工具類
@Data
@Slf4j
@Component
public class MailUtil {

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

    /**
     * 發(fā)送簡單郵件
     * @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ā)送驗證碼為例
@Async("myExecutor") 
public void sendCodeMail(String email) {
    //六位隨機數(shù)的驗證碼
    String code = CodeUtil.randomCode(6);
    //郵件主題
    String subject = "驗證碼";
    //郵件內(nèi)容
    String text = "驗證碼:" + code +"某宪,10分鐘之內(nèi)有效仿村。";

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

    //將驗證碼緩存進redis中,并設(shè)置過期時間兴喂,方便后續(xù)做校驗
    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ā)送郵件蔼囊,可能會比較耗時間,所以需要異步調(diào)用衣迷。
 * 這里的處理方式是畏鼓,配置一個線程池,通過@Async("myExecutor")壶谒,另啟線程調(diào)用云矫。
 * 偷懶的話,可以在啟動類添加注解@EnableAsync汗菜,再在方法上用注解@Async让禀,也可以開啟異步調(diào)用。
 */
 
/**
 * 限制一分鐘內(nèi)只能發(fā)一條陨界,避免惡意攻擊
 */
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(){
    //發(fā)給誰
    String email = "";
    //限制一分鐘內(nèi)只能發(fā)一條巡揍,避免惡意攻擊
    if(checkSendToLegal(email)){
        log.error("操作過于頻繁,請一分鐘后再試菌瘪!");
    }
    //發(fā)送
    sendCodeMail(email);
}

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

7.發(fā)送復雜郵件腮敌,以給郵箱發(fā)送激活鏈接為例
  • 在工具類中添加如下方法
/**
 * 發(fā)送定制郵件
 * @param template 模板
 */
public void sendMimeMail(MailTemplate template){
  try {
      //true表示支持復雜類型
      MimeMessageHelper messageHelper = new MimeMessageHelper(mailSender.createMimeMessage(), true);
      //郵件發(fā)信人
      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());
      }
      //郵件內(nèi)容,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);
          }
      }
      //發(fā)送時間
      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;
    //回復給誰
    private String replyTo;
    //抄送
    private String[] cc;
    //密送
    private String[] bcc;
    //發(fā)送時間
    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;
    }
}

  • 添加一個發(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>點擊下面的按鈕即可激活賬戶</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信息弊添,校驗緩存中是否存在
 */
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生產(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("激活失敗,認證信息已經(jīng)失效捌木!");
    }
    // todo 激活賬戶相關(guān)業(yè)務(wù)

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

效果截圖:


激活鏈接
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钮莲,隨后出現(xiàn)的幾起案子免钻,更是在濱河造成了極大的恐慌彼水,老刑警劉巖崔拥,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凤覆,居然都是意外死亡链瓦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門盯桦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慈俯,“玉大人,你說我怎么就攤上這事拥峦√欤” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵略号,是天一觀的道長刑峡。 經(jīng)常有香客問我,道長玄柠,這世上最難降的妖魔是什么突梦? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮羽利,結(jié)果婚禮上宫患,老公的妹妹穿的比我還像新娘。我一直安慰自己这弧,他們只是感情好娃闲,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匾浪,像睡著了一般皇帮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上户矢,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天玲献,我揣著相機與錄音,去河邊找鬼。 笑死捌年,一個胖子當著我的面吹牛瓢娜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播礼预,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼眠砾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了托酸?” 一聲冷哼從身側(cè)響起褒颈,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎励堡,沒想到半個月后谷丸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡应结,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年刨疼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹅龄。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡揩慕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扮休,到底是詐尸還是另有隱情迎卤,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布玷坠,位于F島的核電站蜗搔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏侨糟。R本人自食惡果不足惜碍扔,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秕重。 院中可真熱鬧不同,春花似錦、人聲如沸溶耘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凳兵。三九已至百新,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間庐扫,已是汗流浹背饭望。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工仗哨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铅辞。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓厌漂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斟珊。 傳聞我的和親對象是個殘疾皇子苇倡,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容