使用SpringBoot Mail
maven导入依赖配置
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> <version>2.1.5.RELEASE</version> </dependency>
|
设置配置文件相关参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring.mail.default-encoding=UTF-8
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.protocol=smtp
spring.mail.username=business@felord.cn
spring.mail.password=oooooxxxxxxxx
spring.mail.jndi-name=
spring.mail.properties.<key> =
spring.mail.test-connection=false
|
配置使用相关服务
发送文本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.nowcoder.community.util;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component;
import javax.mail.MessagingException; import javax.mail.internet.MimeMessage;
@Component public class MailClient {
private static final Logger logger = LoggerFactory.getLogger(MailClient.class);
@Autowired private JavaMailSender mailSender;
@Value("${spring.mail.username}") private String from;
public void sendMail(String to, String subject, String content) { try { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(content, true); mailSender.send(helper.getMimeMessage()); } catch (MessagingException e) { logger.error("发送邮件失败:" + e.getMessage()); } }
}
|
不管是简单的文本还是富文本或者带附件的邮件,我们都可以统一的使用MineMessage
来进行相关配置。
如果使用HTML富文本,需要在setText
方法中将html
置为true
,如上所示,其中from为发送邮箱,to为收件人,content为对应(富)文本。
发送附件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public void sendMailWithAttachment(String to, String subject, String text, String filePath) throws MessagingException {
File attachment = new File(filePath); MimeMessage mimeMessage = javaMailSender.createMimeMessage(); MimeMessageHelper helper=new MimeMessageHelper(mimeMessage,true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(text); helper.addAttachment(attachment.getName(),attachment); javaMailSender.send(mimeMessage);
}
|
牛客论坛项目实战
首先Controller模块部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @RequestMapping(path = "/register", method = RequestMethod.GET) public String getRegisterPage() { return "/site/register"; }
@RequestMapping(path = "/login", method = RequestMethod.GET) public String getLoginPage() { return "/site/login"; }
@RequestMapping(path = "/register", method = RequestMethod.POST) public String register(Model model, User user) { Map<String, Object> map = userService.register(user); if (map == null || map.isEmpty()) { model.addAttribute("msg", "注册成功,我们已经向您的邮箱发送了一封激活邮件,请尽快激活!"); model.addAttribute("target", "/index"); return "/site/operate-result"; } else { model.addAttribute("usernameMsg", map.get("usernameMsg")); model.addAttribute("passwordMsg", map.get("passwordMsg")); model.addAttribute("emailMsg", map.get("emailMsg")); return "/site/register"; } }
|
通过调用UserService
来进行注册相关的操作,包括检查传入数据是否合法,并且处理了数据库和发送邮件操作,现将用户相关信息录入数据库,置用户状态为0,等到激活完成再修改为1,并通过Map的方式将各状态信息进行返回并在网页上打印显示。注册过程中,这里使用了UUID的方式从牛客网服务器随机生成头像url,合同email相关信息注入context
,通过Thymeleaf
直接替换了原先的网页模版。
UserService具体实现逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
| package com.nowcoder.community.service;
import com.nowcoder.community.dao.LoginTicketMapper; import com.nowcoder.community.dao.UserMapper; import com.nowcoder.community.entity.LoginTicket; import com.nowcoder.community.entity.User; import com.nowcoder.community.util.CommunityConstant; import com.nowcoder.community.util.CommunityUtil; import com.nowcoder.community.util.MailClient; import com.nowcoder.community.util.RedisKeyUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Service; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context;
import java.util.*; import java.util.concurrent.TimeUnit;
@Service public class UserService implements CommunityConstant {
@Autowired private UserMapper userMapper;
@Autowired private MailClient mailClient;
@Autowired private TemplateEngine templateEngine;
@Value("${community.path.domain}") private String domain;
@Value("${server.servlet.context-path}") private String contextPath;
@Autowired private RedisTemplate redisTemplate;
public User findUserById(int id) {
User user = getCache(id); if (user == null) { user = initCache(id); } return user; }
public Map<String, Object> register(User user) { Map<String, Object> map = new HashMap<>();
if (user == null) { throw new IllegalArgumentException("参数不能为空!"); } if (StringUtils.isBlank(user.getUsername())) { map.put("usernameMsg", "账号不能为空!"); return map; } if (StringUtils.isBlank(user.getPassword())) { map.put("passwordMsg", "密码不能为空!"); return map; } if (StringUtils.isBlank(user.getEmail())) { map.put("emailMsg", "邮箱不能为空!"); return map; }
User u = userMapper.selectByName(user.getUsername()); if (u != null) { map.put("usernameMsg", "该账号已存在!"); return map; }
u = userMapper.selectByEmail(user.getEmail()); if (u != null) { map.put("emailMsg", "该邮箱已被注册!"); return map; }
user.setSalt(CommunityUtil.generateUUID().substring(0, 5)); user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt())); user.setType(0); user.setStatus(0); user.setActivationCode(CommunityUtil.generateUUID()); user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000))); user.setCreateTime(new Date()); userMapper.insertUser(user);
Context context = new Context(); context.setVariable("email", user.getEmail()); String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode(); context.setVariable("url", url); String content = templateEngine.process("/mail/activation", context); mailClient.sendMail(user.getEmail(), "激活账号", content);
return map; }
public int activation(int userId, String code) { User user = userMapper.selectById(userId); if (user.getStatus() == 1) { return ACTIVATION_REPEAT; } else if (user.getActivationCode().equals(code)) { userMapper.updateStatus(userId, 1); clearCache(userId); return ACTIVATION_SUCCESS; } else { return ACTIVATION_FAILURE; } }
public Map<String, Object> login(String username, String password, long expiredSeconds) { Map<String, Object> map = new HashMap<>();
if (StringUtils.isBlank(username)) { map.put("usernameMsg", "账号不能为空!"); return map; } if (StringUtils.isBlank(password)) { map.put("passwordMsg", "密码不能为空!"); return map; }
User user = userMapper.selectByName(username); if (user == null) { map.put("usernameMsg", "该账号不存在!"); return map; }
if (user.getStatus() == 0) { map.put("usernameMsg", "该账号未激活!"); return map; }
password = CommunityUtil.md5(password + user.getSalt()); if (!user.getPassword().equals(password)) { map.put("passwordMsg", "密码不正确!"); return map; }
LoginTicket loginTicket = new LoginTicket(); loginTicket.setUserId(user.getId()); loginTicket.setTicket(CommunityUtil.generateUUID()); loginTicket.setStatus(0); loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket()); redisTemplate.opsForValue().set(redisKey, loginTicket);
map.put("ticket", loginTicket.getTicket()); return map; }
public void logout(String ticket) {
String redisKey = RedisKeyUtil.getTicketKey(ticket); LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey); loginTicket.setStatus(1); redisTemplate.opsForValue().set(redisKey, loginTicket); }
public LoginTicket findLoginTicket(String ticket) {
String redisKey = RedisKeyUtil.getTicketKey(ticket); return (LoginTicket) redisTemplate.opsForValue().get(redisKey); }
public int updateHeader(int userId, String headerUrl) {
int rows = userMapper.updateHeader(userId, headerUrl); clearCache(userId); return rows; }
public User findUserByName(String username) { return userMapper.selectByName(username); }
private User getCache(int userId) { String redisKey = RedisKeyUtil.getUserKey(userId); return (User) redisTemplate.opsForValue().get(redisKey); }
private User initCache(int userId) { User user = userMapper.selectById(userId); String redisKey = RedisKeyUtil.getUserKey(userId); redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS); return user; }
private void clearCache(int userId) { String redisKey = RedisKeyUtil.getUserKey(userId); redisTemplate.delete(redisKey); }
public Collection<? extends GrantedAuthority> getAuthorities(int userId) { User user = this.findUserById(userId);
List<GrantedAuthority> list = new ArrayList<>(); list.add(new GrantedAuthority() {
@Override public String getAuthority() { switch (user.getType()) { case 1: return AUTHORITY_ADMIN; case 2: return AUTHORITY_MODERATOR; default: return AUTHORITY_USER; } } }); return list; }
}
|
邮件模版如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <!doctype html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/> <title>牛客网-激活账号</title> </head> <body> <div> <p> <b th:text="${email}">xxx@xxx.com</b>, 您好! </p> <p> 您正在注册牛客网, 这是一封激活邮件, 请点击 <a th:href="${url}">此链接</a>, 激活您的牛客账号! </p> </div> </body> </html>
|
在发送邮件时,我们直接调用了已经实现的工具类MailClient
,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package com.nowcoder.community.util;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component;
import javax.mail.MessagingException; import javax.mail.internet.MimeMessage;
@Component public class MailClient {
private static final Logger logger = LoggerFactory.getLogger(MailClient.class);
@Autowired private JavaMailSender mailSender;
@Value("${spring.mail.username}") private String from;
public void sendMail(String to, String subject, String content) { try { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(content, true); mailSender.send(helper.getMimeMessage()); } catch (MessagingException e) { logger.error("发送邮件失败:" + e.getMessage()); } }
}
|
当用户访问邮件内含有的url时,通过UserService
中的activation
方法,对用户的情况进行一个核实,如果配对成功并且状态仍然为0,则将其置1完成激活。