赞
踩
本文的码云地址:点击传送
本文项目效果:
- 发送普通邮件
- 发送随机验证码邮件并把验证码缓存到redis
- 发送带格式模板的邮件
本项目代码都在码云里,传送门在上面,我这里把几个重点的地方详讲一下,所贴代码会删减一部分不重要的,以便阅读
application.yml
【注意】如果是腾讯个人qq邮箱,这里的密码不是邮箱登录密码,而是授权码,否则将会出现密码不对的情况。
授权码:网页QQ邮箱 > 设置 > 账户 > POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 > 开启STMP并获得授权码
server: port: 8080 #邮箱验证码有效时间/秒 code: expiration: 300 # 电子邮件配置 # 如果是腾讯个人qq邮箱,这里的密码不是邮箱登录密码,而是授权码: # 授权码:网页QQ邮箱 > 设置 > 账户 > POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 > 开启STMP并获得授权码 email: host: smtp.qiye.aliyun.com # SMTP地址 port: 465 from: 昵称<xxx@xxx.com> # 发件人昵称 pass: xxxxxx user: xxx@xxx.com # 邮箱地址 # 验证码有效时长(秒) verification: expiration-time: 600 spring: redis: database: 2 host: xxx.xxx.xxx.xxx port: xxxxx password: xxxxxx timeout: 10000ms #是否开启 swagger-ui swagger: enabled: true
这个配置类从上面的配置文件中读取数据填入到属性中。
import io.swagger.annotations.ApiModelProperty; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.persistence.Entity; @Component public class EmailConfig { /** * 发送方 邮件服务器 SMTP 地址 */ @Value("${email.host}") @ApiModelProperty(value = "发送方 邮件服务器SMTP地址") private String host; /** * 发送方 邮件服务器 SMTP 端口 */ @Value("${email.port}") @ApiModelProperty(value = "发送方 邮件服务器 SMTP 端口") private String port; /** * 发送方 昵称 */ @Value("${email.from}") @ApiModelProperty(value = "发送方 昵称") private String from; /** * 发送方 密码 */ @Value("${email.pass}") @ApiModelProperty(value = "发送方 密码") private String pass; /** * 发送方 邮箱地址 */ @Value("${email.user}") @ApiModelProperty(value = "发送方 邮箱地址") private String user; //--TODO 此处省略 Get、Set 方法 }
【注意1】这个类需要加 @Component
注解才能被 Spring 扫描到
【注意2】这个类直接 new 时,属性中的值是空的,是因为被 new 是我们主动创建而不是被 Spring 创建出来的,所以属性值并没有被注入。所以我们需要在使用时使用 @Autowired
注解,被 Spring 托管。
本项目中,采用了一个巧妙的方式,即:该配置类可外部设置并以参数形式传入,如果该参数为null,那么这个配置就有 Spring 负责注入配置文件的数据,等会儿在下方的 4【EmailServiceImpl.java】就能看到。
@Service public class EmailServiceImpl implements EmailService { /** * 验证码有效时长(秒) */ @Value("${verification.expiration-time}") private Long expirationTime; @Autowired private RedisUtils redisUtils; // 这里就是交由 Spring 托管的配置类,为默认配置 @Autowired private EmailConfig defaultConfig; /** * 发送邮件 * @param emailVo 邮件发送的内容 * @param emailConfig 邮件配置,null:使用默认配置 * @throws Exception / */ @Override @Async // 发送邮件为异步执行 @Transactional(rollbackFor = Exception.class) public void send(EmailVo emailVo, EmailConfig emailConfig) { // 这里可以选择使用外部传入的配置,如果外部传入为null,那么便使用默认配置。 if(emailConfig == null) { emailConfig = defaultConfig; } else if (StringUtils.isBlank(emailConfig.getHost()) || StringUtils.isBlank(emailConfig.getPort()) || StringUtils.isBlank(emailConfig.getFrom()) || StringUtils.isBlank(emailConfig.getPass()) || StringUtils.isBlank(emailConfig.getUser())) { throw new BadConfigurationException("请先配置邮箱数据,再操作!"); } MailAccount account = new MailAccount(); // 发件人昵称 account.setFrom(emailConfig.getFrom()); // 发件人邮箱地址 account.setUser(emailConfig.getUser()); account.setHost(emailConfig.getHost()); account.setPort(Integer.parseInt(emailConfig.getPort())); account.setAuth(true); account.setPass(emailConfig.getPass()); // ssl 方式发送 account.setSslEnable(true); // 使用STARTTLS 安全连接 account.setStarttlsEnable(true); // 发送 try { int size = emailVo.getTos().size(); Mail.create(account) .setTos(emailVo.getTos().toArray(new String[size])) .setTitle(emailVo.getSubject()) .setContent(emailVo.getContent()) .setHtml(true) // 关闭session .setUseGlobalSession(false) .send(); } catch (Exception e) { throw new BadRequestException(e.getMessage()); } } /** * 获取验证码的邮件参数类 * @param toEmail 目标邮箱 * @return */ @Override @Transactional(rollbackFor = Exception.class) public EmailVo getVerificationCodeEmailVo(String toEmail) { String content; String redisKey = EmailCodeEnum.VERIFICATION_CODE.getKey() + toEmail; TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); Template template = engine.getTemplate("email/verificationCode.ftlh"); Object oldCode = redisUtils.get(redisKey); // 如果Redis不存在有效验证码,就创建一个新的 // 存在就使用原来的验证码,并将有效期更新 if (oldCode == null) { String code = RandomUtil.randomNumbers(6); // 6位验证码,修改这里可以更改位数 // 存入缓存 boolean saveResult = redisUtils.set(redisKey, code, expirationTime); if(!saveResult) { throw new RedisException("Redis服务异常! 验证码未保存成功!"); } content = template.render(Dict.create().set("code", code)); } else { // 重置Redis中这个key的有效期 redisUtils.expire(redisKey, expirationTime); content = template.render(Dict.create().set("code", oldCode)); } return new EmailVo(Collections.singletonList(toEmail), "Hutool-" + EmailCodeEnum.VERIFICATION_CODE.getDescription(), content); } /** * 获取简单通知的邮件参数类 * @param toEmail 目标邮箱 * @return */ @Override @Transactional(rollbackFor = Exception.class) public EmailVo getSimpleNotificationEmailVo(String toEmail, String msg) { String redisKey = EmailCodeEnum.VERIFICATION_CODE.getKey() + toEmail; TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); Template template = engine.getTemplate("email/simple_notification.ftlh"); String content = template.render(Dict.create().set("msg", msg)); return new EmailVo(Collections.singletonList(toEmail), "Hutool-" + EmailCodeEnum.NOTIFICATION.getDescription(), content); } }
很显然,控制器没什么好讲的~
如果代码写的很完善了,那么代码中的很多校验都可以删除或者放到前端,比如:判断目标邮箱、邮件标题等不为空等。
@RestController @RequestMapping("api/email") @Api(tags = "工具:邮件管理") public class EmailController { @Autowired private EmailService emailService; /** * 发送邮件 * @param toEmail 目标邮箱 * @param subject 邮件标题 * @param content 邮件内容 * @return */ @PostMapping("/sendMail") public String SendEmail(@RequestParam String toEmail, @RequestParam String subject, @RequestParam String content){ if(StringUtils.isBlank(toEmail)) { throw new BadRequestException("目标邮箱不能为空!"); } if(StringUtils.isBlank(subject)) { throw new BadRequestException("邮件标题不能为空!"); } if(StringUtils.isBlank(content)) { throw new BadRequestException("邮件内容不能为空!"); } List<String> toEmails = new ArrayList<>(); toEmails.add(toEmail); EmailVo emailVo = new EmailVo(toEmails, subject, content); emailService.send(emailVo, null); return "邮件已发送!"; } /** * 发送验证码 * @param toEmail 目标邮箱 * @return */ @PostMapping("/sendVerificationCode") public String SendVerificationCode(@RequestParam String toEmail) { EmailVo emailVo = emailService.getVerificationCodeEmailVo(toEmail); emailService.send(emailVo, null); return "邮件已发送!"; } /** * 发送验证码 * @param toEmail 目标邮箱 * @param msg 通知的信息 * @return */ @PostMapping("/sendSimpleNotification") public String SendSimpleNotification(@RequestParam String toEmail, @RequestParam String msg) { if(StringUtils.isBlank(msg)) { throw new BadRequestException("通知信息为空!"); } EmailVo emailVo = emailService.getSimpleNotificationEmailVo(toEmail, msg); emailService.send(emailVo, null); return "邮件已发送!"; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。