From e112a7b5fef3d004575036d1088c58c65203e612 Mon Sep 17 00:00:00 2001 From: dataprince Date: Sat, 27 Jan 2024 10:57:32 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9B=86=E6=88=90sms4j=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E8=81=9A=E5=90=88=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 12 +-- .../web/controller/CaptchaController.java | 28 +++++++ .../src/main/resources/application-dev.yml | 69 +++++++++------- .../src/main/resources/application-prod.yml | 54 +++++++++++++ ruoyi-common/pom.xml | 1 + ruoyi-common/ruoyi-common-bom/pom.xml | 9 ++- ruoyi-common/ruoyi-common-sms/pom.xml | 33 ++++++++ .../sms/config/SmsAutoConfiguration.java | 24 ++++++ .../ruoyi/common/sms/core/dao/FlexSmsDao.java | 72 +++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + ruoyi-modules/ruoyi-demo/pom.xml | 5 ++ .../ruoyi/demo/controller/SmsController.java | 81 +++++++++++++++++++ 12 files changed, 355 insertions(+), 34 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-sms/pom.xml create mode 100644 ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/SmsAutoConfiguration.java create mode 100644 ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/dao/FlexSmsDao.java create mode 100644 ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java diff --git a/pom.xml b/pom.xml index efc68bd..6b4ce3d 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 1.77 - 3.1.0 + 3.1.1 3.0.2 @@ -358,11 +358,11 @@ - - - - - + + org.dromara.sms4j + sms4j-spring-boot-starter + ${sms4j.version} + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java index c1be4ca..7173881 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java @@ -2,6 +2,7 @@ package com.ruoyi.web.controller; import cn.dev33.satoken.annotation.SaIgnore; import java.time.Duration; +import java.util.LinkedHashMap; import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.generator.CodeGenerator; @@ -22,6 +23,9 @@ import com.ruoyi.web.domain.vo.CaptchaVo; import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.sms4j.api.SmsBlend; +import org.dromara.sms4j.api.entity.SmsResponse; +import org.dromara.sms4j.core.factory.SmsFactory; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -105,4 +109,28 @@ public class CaptchaController } return R.ok(); } + + /** + * 短信验证码 + * + * @param phonenumber 用户手机号 + */ + @RateLimiter(key = "#phonenumber", time = 60, count = 1) + @GetMapping("/resource/sms/code") + public R smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { + String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber; + String code = RandomUtil.randomNumbers(4); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + // 验证码模板id 自行处理 (查数据库或写死均可) + String templateId = ""; + LinkedHashMap map = new LinkedHashMap<>(1); + map.put("code", code); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map); + if (!smsResponse.isSuccess()) { + log.error("验证码短信发送异常 => {}", smsResponse); + return R.fail(smsResponse.getData().toString()); + } + return R.ok(); + } } diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index d54d155..99f9e7b 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -103,42 +103,57 @@ powerjob: max-appended-wf-context-length: 4096 max-result-length: 4096 ---- # sms 短信 支持 华为 阿里云 腾讯云 等等各式各样的短信服务商 -# https://sms4j.com/doc3/ 文档地址 各个厂商可同时使用 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 +# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 sms: - # 标注从yml读取配置 + # 配置源类型用于标定配置来源(interface,yaml) config-type: yaml - is-print: true + # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 + restricted: true + # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 + minute-max: 1 + # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 + account-max: 30 + # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中 blends: - # 自定义的标识,也就是configId这里可以是任意值(最好不要是中文) - tx1: - #厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 - supplier: tencent - #您的accessKey + # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 + # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 + config1: + # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: alibaba + # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 access-key-id: 您的accessKey - #您的accessKeySecret + # 称为accessSecret有些称之为apiSecret access-key-secret: 您的accessKeySecret - #您的短信签名 signature: 您的短信签名 - #模板ID 非必须配置,如果使用sendMessage的快速发送需此配置 - template-id: xxxxxxxx - #短信自动重试间隔时间 默认五秒 - retry-interval: 5 - # 短信重试次数,默认0次不重试,如果你需要短信重试则根据自己的需求修改值即可 - max-retries: 0 - #您的sdkAppId sdk-app-id: 您的sdkAppId - # 自定义的标识,也就是configId这里可以是任意值(最好不要是中文) - tx2: - #厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + config2: + # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 supplier: tencent - #您的accessKey access-key-id: 您的accessKey - #您的accessKeySecret access-key-secret: 您的accessKeySecret - #您的短信签名 signature: 您的短信签名 - #模板ID 非必须配置,如果使用sendMessage的快速发送需此配置 - template-id: xxxxxxxx - #您的sdkAppId sdk-app-id: 您的sdkAppId diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index f8afca2..fc107f6 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -117,3 +117,57 @@ powerjob: allow-lazy-connect-server: false max-appended-wf-context-length: 4096 max-result-length: 4096 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 +# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 +sms: + # 配置源类型用于标定配置来源(interface,yaml) + config-type: yaml + # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 + restricted: true + # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 + minute-max: 1 + # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 + account-max: 30 + # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中 + blends: + # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 + # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 + config1: + # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: alibaba + # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 + access-key-id: 您的accessKey + # 称为accessSecret有些称之为apiSecret + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + config2: + # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: tencent + access-key-id: 您的accessKey + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 6e3c641..f629313 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -24,6 +24,7 @@ ruoyi-common-ratelimiter ruoyi-common-redis ruoyi-common-security + ruoyi-common-sms ruoyi-common-springdoc ruoyi-common-tenant ruoyi-common-web diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 6145dd1..d128a24 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -102,7 +102,14 @@ ruoyi-common-security ${revision} - + + + + com.ruoyi + ruoyi-common-sms + ${revision} + + com.ruoyi diff --git a/ruoyi-common/ruoyi-common-sms/pom.xml b/ruoyi-common/ruoyi-common-sms/pom.xml new file mode 100644 index 0000000..9c5d5aa --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/pom.xml @@ -0,0 +1,33 @@ + + + + com.ruoyi + ruoyi-common + ${revision} + + 4.0.0 + + ruoyi-common-sms + + + ruoyi-common-sms 短信模块 + + + + + + org.dromara.sms4j + sms4j-spring-boot-starter + + + + + com.ruoyi + ruoyi-common-redis + + + + + diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/SmsAutoConfiguration.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/SmsAutoConfiguration.java new file mode 100644 index 0000000..05e5021 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/SmsAutoConfiguration.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.sms.config; + +import com.ruoyi.common.sms.core.dao.FlexSmsDao; +import org.dromara.sms4j.api.dao.SmsDao; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +/** + * 短信配置类 + * + * @author Feng + */ +@AutoConfiguration(after = {RedisAutoConfiguration.class}) +public class SmsAutoConfiguration { + + @Primary + @Bean + public SmsDao smsDao() { + return new FlexSmsDao(); + } + +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/dao/FlexSmsDao.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/dao/FlexSmsDao.java new file mode 100644 index 0000000..a94a0fe --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/dao/FlexSmsDao.java @@ -0,0 +1,72 @@ +package com.ruoyi.common.sms.core.dao; + +import com.ruoyi.common.core.constant.GlobalConstants; +import com.ruoyi.common.redis.utils.RedisUtils; +import org.dromara.sms4j.api.dao.SmsDao; + +import java.time.Duration; + +/** + * SmsDao缓存配置 (使用框架自带RedisUtils实现 协议统一) + *

主要用于短信重试和拦截的缓存 + * + * @author Feng + */ +public class FlexSmsDao implements SmsDao { + + /** + * 存储 + * + * @param key 键 + * @param value 值 + * @param cacheTime 缓存时间(单位:秒) + */ + @Override + public void set(String key, Object value, long cacheTime) { + RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, Duration.ofSeconds(cacheTime)); + } + + /** + * 存储 + * + * @param key 键 + * @param value 值 + */ + @Override + public void set(String key, Object value) { + RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, true); + } + + /** + * 读取 + * + * @param key 键 + * @return 值 + */ + @Override + public Object get(String key) { + return RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * remove + *

根据key移除缓存 + * + * @param key 缓存键 + * @return 被删除的value + * @author :Wind + */ + @Override + public Object remove(String key) { + return RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 清空 + */ + @Override + public void clean() { + RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + "sms:"); + } + +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..e4081a1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.ruoyi.common.sms.config.SmsAutoConfiguration diff --git a/ruoyi-modules/ruoyi-demo/pom.xml b/ruoyi-modules/ruoyi-demo/pom.xml index a301066..984d19e 100644 --- a/ruoyi-modules/ruoyi-demo/pom.xml +++ b/ruoyi-modules/ruoyi-demo/pom.xml @@ -26,6 +26,11 @@ com.ruoyi ruoyi-common-web + + + com.ruoyi + ruoyi-common-sms + com.ruoyi diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java new file mode 100644 index 0000000..52d565f --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java @@ -0,0 +1,81 @@ +package com.ruoyi.demo.controller; + +import lombok.RequiredArgsConstructor; +import com.ruoyi.common.core.core.domain.R; +import org.dromara.sms4j.api.SmsBlend; +import org.dromara.sms4j.api.entity.SmsResponse; +import org.dromara.sms4j.core.factory.SmsFactory; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.LinkedHashMap; + +/** + * 短信演示案例 + * 请先阅读文档 否则无法使用 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/sms") +public class SmsController { + /** + * 发送短信Aliyun + * + * @param phones 电话号 + * @param templateId 模板ID + */ + @GetMapping("/sendAliyun") + public R sendAliyun(String phones, String templateId) { + LinkedHashMap map = new LinkedHashMap<>(1); + map.put("code", "1234"); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); + return R.ok(smsResponse); + } + + /** + * 发送短信Tencent + * + * @param phones 电话号 + * @param templateId 模板ID + */ + @GetMapping("/sendTencent") + public R sendTencent(String phones, String templateId) { + LinkedHashMap map = new LinkedHashMap<>(1); +// map.put("2", "测试测试"); + map.put("1", "1234"); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config2"); + SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); + return R.ok(smsResponse); + } + + /** + * 添加黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/addBlacklist") + public R addBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.joinInBlacklist(phone); + return R.ok(); + } + + /** + * 移除黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/removeBlacklist") + public R removeBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.removeFromBlacklist(phone); + return R.ok(); + } + +}