🚀1 开启POP3/SMTP服务。
1.1 在QQ邮箱的【设置】=>【账户】里开启POP3/SMTP服务。
1.2 得到一串授权码。
🚀2 加入依赖和配置application.properties文件
2.1 加入依赖。
<dependencies>
<!--集成redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>2.6.4</version>
</dependency>
<!--集成邮箱-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.6.4</version>
</dependency>
</dependencies>
2.2 配置application.properties文件。
# 服务端口
server.port=8204
# 改成自己redis的ip地址
spring.redis.host=xxx
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
# 最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
# 配置QQ邮件信息
spring.mail.host=smtp.qq.com
# 发送邮件者信箱
spring.mail.username=XXX@qq.com
# IMAP/SMTP服务时邮箱的密文授权码(之前保存的)
spring.mail.password=XXX
spring.mail.default-encoding=UTF-8
spring.mail.port=465
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.debug=true
🚀3 创建启动类和工具类
3.1 创建启动类ServiceMsmApplication。
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置
@EnableDiscoveryClient
public class ServiceMsmApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceMsmApplication.class, args);
}
}
3.2 创建工具类RandomUtil和RedisConfig。
//用于生成随机验证码
public class RandomUtil {
private static final Random random = new Random();
private static final DecimalFormat fourdf = new DecimalFormat("0000");//4位验证码
private static final DecimalFormat sixdf = new DecimalFormat("000000");//6位验证码
public static String getFourBitRandom() {
return fourdf.format(random.nextInt(10000));
}
public static String getSixBitRandom() {
return sixdf.format(random.nextInt(1000000));
}
// 给定数组,抽取n个数据
public static ArrayList getRandom(List list, int n) {
Random random = new Random();
HashMap<Object, Object> hashMap = new HashMap<Object, Object>();
// 生成随机数字并存入HashMap
for (int i = 0; i < list.size(); i++) {
int number = random.nextInt(100) + 1;
hashMap.put(number, i);
}
// 从HashMap导入数组
Object[] robjs = hashMap.values().toArray();
ArrayList r = new ArrayList();
// 遍历数组并打印数据
for (int i = 0; i < n; i++) {
r.add(list.get((int) robjs[i]));
System.out.print(list.get((int) robjs[i]) + "\t");
}
System.out.print("\n");
return r;
}
}
@Configuration
@EnableCaching
public class RedisConfig {
/**
* 自定义key规则
*
* @return
*/
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 设置RedisTemplate规则
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//序列号key value
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 设置CacheManager缓存规则
*
* @param factory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
🚀4 创建控制层和业务层
4.1 创建控制层MailController。
@RestController
@RequestMapping("api/email")
public class MailController {
@Autowired
private MailService mailService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
//发送邮箱验证码
@GetMapping("send/{email}")
public Result sendEmail(@PathVariable String email) {
//key 邮箱号 value 验证码
String code = redisTemplate.opsForValue().get(email);
//从redis获取验证码,如果获取获取到,返回ok
if (!StringUtils.isEmpty(code)) {
return Result.ok();
}
//如果从redis获取不到,生成新的6位验证码
code = RandomUtil.getSixBitRandom();
//调用service方法,通过邮箱服务进行发送
boolean isSend = mailService.sendMail(email, code);
//生成验证码放到redis里面,设置有效时间
if (isSend) {
redisTemplate.opsForValue().set(email, code, 5, TimeUnit.MINUTES);
return Result.ok();
} else {
return Result.fail().message("发送邮箱验证码失败");
}
}
}
4.2 创建业务层MailService和MailServiceImpl 。
public interface MailService {
//发送邮件
boolean sendMail(String email, String text);
}
@Service
public class MailServiceImpl implements MailService {
@Value("${spring.mail.username}")
private String UserName;//获得配置文件中的username
@Autowired
private JavaMailSender mailSender;//注入发送邮件的bean
@Override
public boolean sendMail(String email, String code) {
//判断邮箱是否为空
if (StringUtils.isEmpty(email)) {
return false;
}
//标题
String subject = "邮箱验证码";
//正文内容
String text = "你的验证码为" + code + ",有效时间为5分钟,请尽快使用并且不要告诉别人。";
SimpleMailMessage msg = new SimpleMailMessage();
//发送邮件的邮箱
msg.setFrom(UserName);
//发送到哪(邮箱)
msg.setTo(email);
//邮箱标题
msg.setSubject(subject);
//邮箱文本
msg.setText(text);
try {
mailSender.send(msg);
System.out.println("msg=====>" + msg);
} catch (MailException ex) {
ex.getMessage();
}
return true;
}
}
🚀5 全局统一返回结果类
5.1 创建自定义返回类Result
//全局统一返回结果类
@Data
public class Result<T> {
//返回码
private Integer code;
//返回消息
private String message;
//返回数据
private T data;
public Result() {
}
protected static <T> Result<T> build(T data) {
Result<T> result = new Result<T>();
if (data != null)
result.setData(data);
return result;
}
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
public static <T> Result<T> build(Integer code, String message) {
Result<T> result = build(null);
result.setCode(code);
result.setMessage(message);
return result;
}
public static <T> Result<T> ok() {
return Result.ok(null);
}
//操作成功
public static <T> Result<T> ok(T data) {
Result<T> result = build(data);
return build(data, ResultCodeEnum.SUCCESS);
}
public static <T> Result<T> fail() {
return Result.fail(null);
}
//操作失败
public static <T> Result<T> fail(T data) {
Result<T> result = build(data);
return build(data, ResultCodeEnum.FAIL);
}
public Result<T> message(String msg) {
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code) {
this.setCode(code);
return this;
}
public boolean isOk() {
if (this.getCode().intValue() == ResultCodeEnum.SUCCESS.getCode().intValue()) {
return true;
}
return false;
}
}
5.2 创建自定义返回信息枚举类ResultCodeEnum
@Getter
public enum ResultCodeEnum {
SUCCESS(200, "成功"),
FAIL(201, "失败"),
TOKEN_ERROR(400, "HTTP请求失败"),
TOKEN_PARSING_FAILED(401, "用户认证失败"),
USER_CHECK_ERROR(403, "用户权限不足");
private final Integer code;
private final String message;
ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
📋 6 测试方法
6.1 Postman测试。
发送成功