1.生成验证码基本思路
思路:提供接口将生成的验证码存入session或redis,将验证码以图片格式或者base64编码串返回给调用端。
存储以一个固定的KEY 例如:captcha_codes+uuid作为key 验证码的计算结果作为value
pom hutool工具类
<!-- hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.12</version>
</dependency>
先生成一个base64编码的案例
//定义图形验证码的长、宽、验证码字符数、干扰线宽度
ShearCaptcha shearCaptcha1 = CaptchaUtil.createShearCaptcha(150, 40, 5, 0);
//生成的字符用作对比
System.out.println(shearCaptcha1.getCode());
System.out.println(shearCaptcha1.getImageBase64());
base64编码图片
rav01
iVBORw0KGgoAAAANSUhEUgAAAJYAAAAoCAYAAAAcwQPnAAAFd0lEQVR42u2bi09TVxzH/TeWLFu2ZDFZ4h4uy0yWZRlxuhEz4zaHzDEfGzoNG2hhiO6hM5l7CHPEIKBsMBnKBAKC49kCPqACBYQBIlCsQHkMWloE2lL8zd8h96QXSnn0HoTu901OmnPP93Luvf303N/5ncMqIJEEaBU9AhKBRSKwSAQWiURgkQgsEoFFIhFYJAKLNF/1DY9DbF4bfBijhbUHi+G5sEL2uSO2Cs7kt8PQiH1JrqNd3wEb3nmLFQJrhUvT0A8vqYrh2c8LZi0vh5cwn0iNjY9BcMheAstX1N47Ai8cKPIIllTWhBYyvwjZ7XY4/O0RDpY3cBFYy0D74nUcnHWRakjWdIL5voO1DVrt8Ju6UzaiRZ1vUPwaTGYThEaEyaAisHwgvpKgwdhqthGpqm1I5hu1ORW7hhuVFfBB0LYZYC0WLgJrGSirspsDE5Fyy6M3JLGWe//W9SrSvyoqnEO0OWALaKu04sF6suQLXlBnDRpYrVHBi+VRcKw1U+gDn3j4zq+9nA2ZR7+CmM3+8ONGP/hhwxtwctNGyPjmCDSpixXrKzvfH86mPsaKydzi0TtkbuZePM9bRf7RwGG5UmP06M2tNnLv4dRGRe5dAujgIRX0GHtkx4SOWK5waU1tsvqu+gRhYPU0N0Hs1i3wvd9rHsvFSBU8eDDpdX8tbec5MNX1Jzx6tbpj3Nt8J8XrvoPjajgwNe0mj946vZl7MR2hhHbv+xTKrpW7BW4xcC0YrHerf5HVM4w3hUA1MjjIRqm5oJKKNv2C9yPkxBj8fuEpBkta1lqPXmxHX1LaE+CYuO913698WcJhsTk8/0iw3TXYFyXhYE2H62l1GOT21YrP7yTGc3BOb3sfOh6++50TU7OlSacTOnXVkLRnN/ck79+jSL9XtSo+GvX2a916jP0V3FNeEaZIv64ph/lI8uJ5yw2uRYH13Z2sJQlq47YHcGgG9Hq3HstAP/f89PZ6Rfrt/1fHoUHI3OmaNmJO+BYq13zVQvw4Q/QJsMoGm5fVrEoC68T61xX7m5l5fgya5PRnZsRvWE9JX83aL2avU6xPTH7+r8FyTDofGUTjVivU5eawWSGOUNNjLaXU2JLIRyS94bKszdBdxNvq/4lVrE9cG1wMWHjeigXLXephKdVWeQMufR3FUg3TYcI0hAi4bDYTC84RoKKynfL47/p+dvzcn4/D6FifYn2+ekjjE0H8igAr+/hRGTg/+7/JRqzbV8t5MC8CLFeAEDCbfXhq4jDp4MAVlAYp2p/r8k5Lt9Wj99bdYe795HQ1gbUQVWX8JctXmbq7Z3gw5hEFlrHvOn/tYZ6LjZ76S/zY3Xv5ivaHSU8JloJaz5l1zLyLXD/0abASdn7EgcH4yp26GhuEgYXCAB0hulKyldXz1YGsnpqxRpHE7GJhwXbJi8tCKxasRwUXzvYkaEbN5hntuPSTEvKZDC6lv/C6xlM8prJYO9kn1m/WHlf8fnFTnzRDxBlfo2F41leh60wSF7OXC1wrAizXDHxa+AEYNBimYh2nk8VZ54J3zQjoHTZlHzIG6BJMecXv8VfhsKVDyD2HJtXJNvnhVhppFyluqcE6Hpc8e8/UCP0OfBKsglPR817akYqhvk7x6ygs/ZgDhSWnYJOwe27tsc5r45+UeRe1+c+nwRqzWCA+KHBWiPBVqcvOksVjWFdarvkrLLfb04Ted0ZF15w7S3HUKm8aEP4d+CRYKAzcNQlxkLBjO9s6gwVBKvw1Boa67jFPc6mag4Uxl9LCuC0183kGFS5WO5024ffdaxqH6JxWCDhZyROo+BkYrWX/fDFgsS3J8xcOFolEYJEILBKBRSIRWCQCi0RgkUgEFonAIvmK/gOB1MzDx4L9UAAAAABJRU5ErkJggg==
2.ry案例生成验证码思路
代码解读
验证码操作处理类
/**
* 验证码操作处理
*
* @author Lion Li
*/
@SaIgnore
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
public class CaptchaController {
private final CaptchaProperties captchaProperties;
private final ISysConfigService configService;
private final MailProperties mailProperties;
/**
* 短信验证码
*
* @param phonenumber 用户手机号
*/
@GetMapping("/captchaSms")
public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;
String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
// 验证码模板id 自行处理 (查数据库或写死均可)
String templateId = "";
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
map.put("code", code);
SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
if (!"OK".equals(smsResponse.getCode())) {
log.error("验证码短信发送异常 => {}", smsResponse);
return R.fail(smsResponse.getMessage());
}
return R.ok();
}
/**
* 邮箱验证码
*
* @param email 邮箱
*/
@GetMapping("/captchaEmail")
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
if (!mailProperties.getEnabled()) {
return R.fail("当前系统没有开启邮箱功能!");
}
String key = CacheConstants.CAPTCHA_CODE_KEY + email;
String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
try {
MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
} catch (Exception e) {
log.error("验证码短信发送异常 => {}", e.getMessage());
return R.fail(e.getMessage());
}
return R.ok();
}
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public R<Map<String, Object>> getCode() {
Map<String, Object> ajax = new HashMap<>();
//applicalyion 中配置例如captcha: spring初始化时候 将类CaptchaProperties 生成Bean
boolean captchaEnabled = configService.selectCaptchaEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled) {
return R.ok(ajax);
}
// 保存验证码信息
String uuid = IdUtil.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
CaptchaType captchaType = captchaProperties.getType();
//获取验证码类型
boolean isMath = CaptchaType.MATH == captchaType;
//获取验证码长度
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
// CodeGenerator 反射生成验证码生成器
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator);
captcha.createCode();
String code = captcha.getCode();
if (isMath) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class);
}
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
ajax.put("uuid", uuid);
ajax.put("img", captcha.getImageBase64());
return R.ok(ajax);
}
}
2.1 从缓存中读取是否开启的验证码认证
@Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = “#configKey”)
在缓存(可以是Redis也可以是Spring的缓存)中查找NAME为 CacheNames.SYS_CONFIG 的MAP再用EL表达式找 MAP的key为 #configKey 的VALUE
Controller
boolean captchaEnabled = configService.selectCaptchaEnabled();
ServiceImpl
/**
* 获取验证码开关
*
* @return true开启,false关闭
*/
@Override
public boolean selectCaptchaEnabled() {
String captchaEnabled = SpringUtils.getAopProxy(this).selectConfigByKey("sys.account.captchaEnabled");
if (StringUtils.isEmpty(captchaEnabled)) {
return true;
}
return Convert.toBool(captchaEnabled);
}
/**
* 根据键名查询参数配置信息
*
* @param configKey 参数key
* @return 参数键值
*/
@Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey")
@Override
public String selectConfigByKey(String configKey) {
SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getConfigKey, configKey));
if (ObjectUtil.isNotNull(retConfig)) {
return retConfig.getConfigValue();
}
return StringUtils.EMPTY;
}
2.2 生成redis验证码KEY
// 保存验证码信息
String uuid = IdUtil.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
2.3配置类中获取验证码配置
yaml 为了可随意切换验证码类型和干扰类型
captcha:
# 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
配置类 CaptchaProperties.java 获取yaml中的配置
/**
* 验证码 配置属性
*
* @author Lion Li
*/
@Data
@Component
@ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties {
/**
* 验证码类型
*/
private CaptchaType type;
/**
* 验证码类别
*/
private CaptchaCategory category;
/**
* 数字验证码位数
*/
private Integer numberLength;
/**
* 字符验证码长度
*/
private Integer charLength;
}
枚举类 CaptchaType (验证码类型)CaptchaCategory (验证码类别)
/**
* 验证码类型
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum CaptchaType {
/**
* 数字验证码
*/
MATH(UnsignedMathGenerator.class),
/**
* 字符验证码
*/
CHAR(RandomGenerator.class);
private final Class<? extends CodeGenerator> clazz;
}
2.4拿到验证码配置信息
CaptchaType captchaType = captchaProperties.getType();
//获取验证码类型
boolean isMath = CaptchaType.MATH == captchaType;
//获取验证码长度
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
2.5 通过反射生成,字符或者数字的验证码文字生成器 (CodeGenerator)
/**
* 验证码类别
*/
private CaptchaCategory category;
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
//通过条码干扰的bena类型 动态获取抽象验证码的基本属性
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
SpringUtils.getBean(captchaProperties.getCategory().getClazz()) 通过类型拿到干扰验证码配置类
/**
* 验证码配置
*
* @author Lion Li
*/
@Configuration
public class CaptchaConfig {
private static final int WIDTH = 160;
private static final int HEIGHT = 60;
private static final Color BACKGROUND = Color.PINK;
private static final Font FONT = new Font("Arial", Font.BOLD, 48);
/**
* 圆圈干扰验证码
*/
@Lazy
@Bean
public CircleCaptcha circleCaptcha() {
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT);
captcha.setBackground(BACKGROUND);
captcha.setFont(FONT);
return captcha;
}
/**
* 线段干扰的验证码
*/
@Lazy
@Bean
public LineCaptcha lineCaptcha() {
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT);
captcha.setBackground(BACKGROUND);
captcha.setFont(FONT);
return captcha;
}
/**
* 扭曲干扰验证码
*/
@Lazy
@Bean
public ShearCaptcha shearCaptcha() {
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT);
captcha.setBackground(BACKGROUND);
captcha.setFont(FONT);
return captcha;
}
}
2.6 生成验证码保存到redis 返回UUID
//通过条码干扰的bena类型 动态获取抽象验证码的基本属性
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator);
captcha.createCode();
String code = captcha.getCode();
if (isMath) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class);
}
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
ajax.put("uuid", uuid);
ajax.put("img", captcha.getImageBase64());
return R.ok(ajax);
3.验证码登录 Redis获取保存的code比较值是否相同
/**
* 校验验证码
*
* @param username 用户名
* @param code 验证码
* @param uuid 唯一标识
*/
public void validateCaptcha(String username, String code, String uuid) {
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
String captcha = RedisUtils.getCacheObject(verifyKey);
RedisUtils.deleteObject(verifyKey);
if (captcha == null) {
recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) {
recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
throw new CaptchaException();
}
}
4.邮箱验证码登录(注册)
4.1邮箱配置,验证码发送,验证码保存Redis
yaml配置邮箱
--- # mail 邮件发送
mail:
enabled: true
host: smtp.qq.com
port: 465
# 是否需要用户名密码验证
auth: true
# 发送方,遵循RFC-822标准
from: work.****@qq.com
# 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
user: work.****@qq.com
# 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
pass: abcedfg
# 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
starttlsEnable: true
# 使用SSL安全连接
sslEnable: true
# SMTP超时时长,单位毫秒,缺省值不超时
timeout: 0
# Socket连接超时值,单位毫秒,缺省值不超时
connectionTimeout: 0
初始化将参数加载到配置类中
/**
* JavaMail 配置属性
*
* @author Michelle.Chung
*/
@Data
@Component
@ConfigurationProperties(prefix = "mail")
public class MailProperties {
/**
* 过滤开关
*/
private Boolean enabled;
/**
* SMTP服务器域名
*/
private String host;
/**
* SMTP服务端口
*/
private Integer port;
/**
* 是否需要用户名密码验证
*/
private Boolean auth;
/**
* 用户名
*/
private String user;
/**
* 密码
*/
private String pass;
/**
* 发送方,遵循RFC-822标准
*/
private String from;
/**
* 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。
*/
private Boolean starttlsEnable;
/**
* 使用 SSL安全连接
*/
private Boolean sslEnable;
/**
* SMTP超时时长,单位毫秒,缺省值不超时
*/
private Long timeout;
/**
* Socket连接超时值,单位毫秒,缺省值不超时
*/
private Long connectionTimeout;
}
发送验证码
/**
* 邮箱验证码
*
* @param email 邮箱
*/
@GetMapping("/captchaEmail")
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
if (!mailProperties.getEnabled()) {
return R.fail("当前系统没有开启邮箱功能!");
}
String key = CacheConstants.CAPTCHA_CODE_KEY + email;
String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
try {
MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
} catch (Exception e) {
log.error("验证码短信发送异常 => {}", e.getMessage());
return R.fail(e.getMessage());
}
return R.ok();
}
邮箱工具类
/**
* 邮件工具类
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MailUtils {
private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class);
/**
* 获取邮件发送实例
*/
public static MailAccount getMailAccount() {
return ACCOUNT;
}
/**
* 获取邮件发送实例 (自定义发送人以及授权码)
*
* @param user 发送人
* @param pass 授权码
*/
public static MailAccount getMailAccount(String from, String user, String pass) {
ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom()));
ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser()));
ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass()));
return ACCOUNT;
}
/**
* 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
* @param subject 标题
* @param content 正文
* @param files 附件列表
* @return message-id
* @since 3.2.0
*/
public static String sendText(String to, String subject, String content, File... files) {
return send(to, subject, content, false, files);
}
/**
* 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
* @param subject 标题
* @param content 正文
* @param files 附件列表
* @return message-id
* @since 3.2.0
*/
public static String sendHtml(String to, String subject, String content, File... files) {
return send(to, subject, content, true, files);
}
/**
* 使用配置文件中设置的账户发送邮件,发送单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
* @param subject 标题
* @param content 正文
* @param isHtml 是否为HTML
* @param files 附件列表
* @return message-id
*/
public static String send(String to, String subject, String content, boolean isHtml, File... files) {
return send(splitAddress(to), subject, content, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送邮件,发送单个或多个收件人<br>
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
* @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
* @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
* @param subject 标题
* @param content 正文
* @param isHtml 是否为HTML
* @param files 附件列表
* @return message-id
* @since 4.0.3
*/
public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) {
return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送文本邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
* @param files 附件列表
* @return message-id
*/
public static String sendText(Collection<String> tos, String subject, String content, File... files) {
return send(tos, subject, content, false, files);
}
/**
* 使用配置文件中设置的账户发送HTML邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
* @param files 附件列表
* @return message-id
* @since 3.2.0
*/
public static String sendHtml(Collection<String> tos, String subject, String content, File... files) {
return send(tos, subject, content, true, files);
}
/**
* 使用配置文件中设置的账户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
* @param isHtml 是否为HTML
* @param files 附件列表
* @return message-id
*/
public static String send(Collection<String> tos, String subject, String content, boolean isHtml, File... files) {
return send(tos, null, null, subject, content, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
* @param bccs 密送人列表,可以为null或空
* @param subject 标题
* @param content 正文
* @param isHtml 是否为HTML
* @param files 附件列表
* @return message-id
* @since 4.0.3
*/
public static String send(Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, boolean isHtml, File... files) {
return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files);
}
// ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
/**
* 发送邮件给多人
*
* @param mailAccount 邮件认证对象
* @param to 收件人,多个收件人逗号或者分号隔开
* @param subject 标题
* @param content 正文
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @return message-id
* @since 3.2.0
*/
public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) {
return send(mailAccount, splitAddress(to), subject, content, isHtml, files);
}
/**
* 发送邮件给多人
*
* @param mailAccount 邮件帐户信息
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @return message-id
*/
public static String send(MailAccount mailAccount, Collection<String> tos, String subject, String content, boolean isHtml, File... files) {
return send(mailAccount, tos, null, null, subject, content, isHtml, files);
}
/**
* 发送邮件给多人
*
* @param mailAccount 邮件帐户信息
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
* @param bccs 密送人列表,可以为null或空
* @param subject 标题
* @param content 正文
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @return message-id
* @since 4.0.3
*/
public static String send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, boolean isHtml, File... files) {
return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param files 附件列表
* @return message-id
* @since 3.2.0
*/
public static String sendHtml(String to, String subject, String content, Map<String, InputStream> imageMap, File... files) {
return send(to, subject, content, imageMap, true, files);
}
/**
* 使用配置文件中设置的账户发送邮件,发送单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML
* @param files 附件列表
* @return message-id
*/
public static String send(String to, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
return send(splitAddress(to), subject, content, imageMap, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送邮件,发送单个或多个收件人<br>
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
* @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
* @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML
* @param files 附件列表
* @return message-id
* @since 4.0.3
*/
public static String send(String to, String cc, String bcc, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送HTML邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param files 附件列表
* @return message-id
* @since 3.2.0
*/
public static String sendHtml(Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, File... files) {
return send(tos, subject, content, imageMap, true, files);
}
/**
* 使用配置文件中设置的账户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML
* @param files 附件列表
* @return message-id
*/
public static String send(Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
return send(tos, null, null, subject, content, imageMap, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
* @param bccs 密送人列表,可以为null或空
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML
* @param files 附件列表
* @return message-id
* @since 4.0.3
*/
public static String send(Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files);
}
// ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
/**
* 发送邮件给多人
*
* @param mailAccount 邮件认证对象
* @param to 收件人,多个收件人逗号或者分号隔开
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @return message-id
* @since 3.2.0
*/
public static String send(MailAccount mailAccount, String to, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files);
}
/**
* 发送邮件给多人
*
* @param mailAccount 邮件帐户信息
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @return message-id
* @since 4.6.3
*/
public static String send(MailAccount mailAccount, Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files);
}
/**
* 发送邮件给多人
*
* @param mailAccount 邮件帐户信息
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
* @param bccs 密送人列表,可以为null或空
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @return message-id
* @since 4.6.3
*/
public static String send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap,
boolean isHtml, File... files) {
return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files);
}
/**
* 根据配置文件,获取邮件客户端会话
*
* @param mailAccount 邮件账户配置
* @param isSingleton 是否单例(全局共享会话)
* @return {@link Session}
* @since 5.5.7
*/
public static Session getSession(MailAccount mailAccount, boolean isSingleton) {
Authenticator authenticator = null;
if (mailAccount.isAuth()) {
authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass());
}
return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) //
: Session.getInstance(mailAccount.getSmtpProps(), authenticator);
}
// ------------------------------------------------------------------------------------------------------------------------ Private method start
/**
* 发送邮件给多人
*
* @param mailAccount 邮件帐户信息
* @param useGlobalSession 是否全局共享Session
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
* @param bccs 密送人列表,可以为null或空
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符,占位符格式为cid:${cid}
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @return message-id
* @since 4.6.3
*/
private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content,
Map<String, InputStream> imageMap, boolean isHtml, File... files) {
final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession);
// 可选抄送人
if (CollUtil.isNotEmpty(ccs)) {
mail.setCcs(ccs.toArray(new String[0]));
}
// 可选密送人
if (CollUtil.isNotEmpty(bccs)) {
mail.setBccs(bccs.toArray(new String[0]));
}
mail.setTos(tos.toArray(new String[0]));
mail.setTitle(subject);
mail.setContent(content);
mail.setHtml(isHtml);
mail.setFiles(files);
// 图片
if (MapUtil.isNotEmpty(imageMap)) {
for (Map.Entry<String, InputStream> entry : imageMap.entrySet()) {
mail.addImage(entry.getKey(), entry.getValue());
// 关闭流
IoUtil.close(entry.getValue());
}
}
return mail.send();
}
/**
* 将多个联系人转为列表,分隔符为逗号或者分号
*
* @param addresses 多个联系人,如果为空返回null
* @return 联系人列表
*/
private static List<String> splitAddress(String addresses) {
if (StrUtil.isBlank(addresses)) {
return null;
}
List<String> result;
if (StrUtil.contains(addresses, CharUtil.COMMA)) {
result = StrUtil.splitTrim(addresses, CharUtil.COMMA);
} else if (StrUtil.contains(addresses, ';')) {
result = StrUtil.splitTrim(addresses, ';');
} else {
result = CollUtil.newArrayList(addresses);
}
return result;
}
// ------------------------------------------------------------------------------------------------------------------------ Private method end
}