这个网站搭建也快有两年了,这两年虽然没有什么产出,但是经过两年的积累也算是收益颇丰。因为一开始只是打算用来做一些笔记,并没有考虑网站本身的交互性,评论几乎都是置之不理,前几天突然心血来潮查了一下评论表,居然收获了上百条的评论,然而这些评论中的绝大多数我都是第一次见,其中不乏一些加油以及对知识点提出疑问的评论,我要在这里向留下评论的你们表示感谢。但是光是感谢是不够的,我要提高网站的交互性,对于这些评论我要第一时间知道,如何第一时间知道?想必脑海中冒出来的第一个念头就是短信了吧,打开腾讯云搜索短信,我又打开了一片新世界。
腾讯云短信平台
短信签名和短信模板被驳回了2次。。。我也是醉了,不过结果还好审核通过了,并发现了一个小惊喜,腾讯云个人认证账户每个月可以白嫖100条的免费短信,very good.点开开发者指南去学习如何引入SDK发短信就好啦,中文的文档不会还有人看不懂吧,不会吧不会吧。。
引入SDK 腾讯云短信接入文档 我把文档看完了,在这里做个小总结,首先你需要把SDK的依赖引入到工程的POM中,想要发短信的话还需要实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId 和 secretKey: 还需要实例化一个发短信的客户端:
最后还需要将短信的详细信息封装到一个对象中
最后使用短信客户端把短信的详细信息发送出去就好了吧,就这样**client.SendSms(req)**嗖的一下就发出去了。
引入依赖
代码语言:javascript复制<!-- https://mvnrepository.com/artifact/com.tencentcloudapi/tencentcloud-sdk-java -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>4.0.11</version>
</dependency>
创建对象
emm,认证对象只要传入两个固定的参数就好啦非常ESAY,所以就把他交给Spring管理就好啦,这个客户端对象一个系统也有一个就可以啦,那就单例模式搞一个,emm~~短信对象创建起来有点复杂,而且要根据不同的评论进行定制,所以工厂模式安排!
注入IOC容器
代码语言:javascript复制package com.jmy.config;
import com.jmy.factory.SmsClientBeanFactory;
import com.jmy.util.SmsUtils;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.sms.v20190711.SmsClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SmsConfig {
@Bean
public Credential credential(){
return new Credential(SmsUtils.SECRETID, SmsUtils.SECRETKEY);
}
@Bean
public SmsClient smsClient(Credential credential){
return SmsClientBeanFactory.getSmsClientInstance(credential);
}
}
单例模式创建SmsClient
代码语言:javascript复制package com.jmy.config;
import com.jmy.factory.SmsClientBeanFactory;
import com.jmy.util.SmsUtils;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.sms.v20190711.SmsClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SmsConfig {
@Bean
public Credential credential(){
return new Credential(SmsUtils.SECRETID, SmsUtils.SECRETKEY);
}
@Bean
public SmsClient smsClient(Credential credential){
return SmsClientBeanFactory.getSmsClientInstance(credential);
}
}
工厂模式创建SendSmsRequestBeanFactory
代码语言:javascript复制package com.jmy.factory;
import com.jmy.util.SmsUtils;
import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
public class SendSmsRequestBeanFactory{
public static SendSmsRequest getInstance() {
SendSmsRequest req = new SendSmsRequest();
req.setSmsSdkAppid(SmsUtils.APPID);
req.setSign(SmsUtils.SIGN);
req.setSenderId(SmsUtils.SENDERID);
req.setTemplateID(SmsUtils.TEMPLATEID);
req.setPhoneNumberSet(SmsUtils.PHONENUMBERS);
return req;
}
}
编写切面接入短信功能
评论发送短信通知博主,这功能一听就是与评论业务无关只是对评论功能单纯的一个增强,这不正是面向切面编程的意义所在吗?心动不如行动,还在有犹豫什么呢,快去编写发送短信的切面吧。
后置通知发送短信
代码语言:javascript复制/**
* Created by jmy on 2021/4/29.
*/
@Aspect
@Component
public class SmsAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private SmsClient smsClient;
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Pointcut("@annotation(com.jmy.aspect.SendMessage)")
public void sendMessage(){}
@After("sendMessage()")
public void doAfter(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
Comment comment = (Comment) args[];
// 发送短信
SendSmsRequest req = SendSmsRequestBeanFactory.getInstance();
/* 模板参数: 若无模板参数,则设置为空*/
String title = comment.getBlog().getTitle();
String[] templateParams = {title.length() > ? title.substring(,):title};
req.setTemplateParamSet(templateParams);
HttpServletRequest request = RepeatCommentInterceptor.threadLocal.get();
String tll = RepeatCommentInterceptor.TLL.get();
String remoteAddr = request.getRemoteAddr();
new Thread(() -> {
try {
String boo = redisTemplate.opsForValue().get("comment:ip:" remoteAddr);
if (!StringUtils.isEmpty(boo) && tll.equals(boo)) {
smsClient.SendSms(req);
}
} catch (TencentCloudSDKException e) {
logger.error(e.getMessage());
}
}).start();
}
}
切面写好了怎么通知到我们的评论功能呢?当然是按照我们编写好的切入规则@Pointcut(“@annotation(com.jmy.aspect.SendMessage)”),在评论的方法上加上我们自定义的SendMessage注解就好啦。
不讲武德,耗子尾汁
短信通知的功能倒是实现啦,但是我眉头一紧突然意识到一个严重的问题,如果有不讲武德的小伙子刷评论我这白嫖的100条短信不是很快就没了吗,所以要小小的改造一下加个拦截器将请求的IP地址放到Redis中保存一小段时间用来校验是否刷评论就可以了,如何在拦截器中注入Spring管理的对象以及如何在切面中如何使用Request对象,这些都是不可忽视的小细节哦.
代码语言:javascript复制/**
* Created by jmy on 2021/4/30.
* 重复评论短信拦截器
*/
public class RepeatCommentInterceptor implements HandlerInterceptor {
public static ThreadLocal<HttpServletRequest> threadLocal = new ThreadLocal<>();
public static ThreadLocal<String> TLL = new ThreadLocal<>();
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
threadLocal.set(httpServletRequest);
String remoteAddr = httpServletRequest.getRemoteAddr();
String tll = UUID.randomUUID().toString();
TLL.set(tll);
redisTemplate.opsForValue().setIfAbsent("comment:ip:" remoteAddr, tll);
redisTemplate.expire("comment:ip:" remoteAddr,,TimeUnit.MINUTES);
return true;
}
代码语言:javascript复制/**
* Created by jmy on 2021/4/30.
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public RepeatCommentInterceptor repeatCommentInterceptor(){
return new RepeatCommentInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin")
.excludePathPatterns("/admin/login");
registry.addInterceptor(repeatCommentInterceptor())
.addPathPatterns("/comments");
}
}