book-api 创建 PassportController
@Slf4j
@Api(tags = "Passport通行")
@RequestMapping("passport")//映射路由地址
@RestController
public class PassportController extends BaseInfoProperties {
@Autowired
private SMSUtils smsUtils;
@Autowired
private UserService userService;
@PostMapping("getSMSCode")
public GraceJSONResult getSMSCode(@RequestParam String mobile,
HttpServletRequest request) throws Exception {
if (StringUtils.isBlank(mobile)){
return GraceJSONResult.errorMsg("请输入手机号");
}
//获取用户IP
String userIp = IPUtil.getRequestIp(request);
log.info(userIp);
String keyIp = MOBILE_SMSCODE + ":" + userIp;
//根据用户IP 进行限制,限制用户在60s之内只能获取一次验证码
redis.setnx60s(keyIp,userIp);
String code = (int)((Math.random() * 9 + 1 ) * 100000) + "";
// smsUtils.sendSMS(mobile,code);
log.info(code);
// 把验证码放入到redis中,用于后续的验证
String mobileRedisKey = MOBILE_SMSCODE + ":" + mobile;
redis.set(MOBILE_SMSCODE + ":" + mobile,code, 30*60);
return GraceJSONResult.ok();
}
}
整合腾讯云短信
- tencentcloud.properties
tencent.cloud.secretId=xxx
tencent.cloud.secretKey=xx
2. TencentCloudProperties
package org.lz.utils;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@Data
@PropertySource("classpath:tencentcloud.properties")
@ConfigurationProperties(prefix = "tencent.cloud")
public class TencentCloudProperties {
private String secretId;
private String secretKey;
}
book-common utils 创建 SMSUtils
package org.lz.utils;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SMSUtils {
@Autowired
private TencentCloudProperties tencentCloudProperties;
public void sendSMS(String phone, String code) throws Exception {
try {
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId,SecretKey。
// 为了保护密钥安全,建议将密钥设置在环境变量中或者配置文件中,请参考凭证管理 https://github.com/TencentCloud/tencentcloud-sdk-java?tab=readme-ov-file#%E5%87%AD%E8%AF%81%E7%AE%A1%E7%90%86。
// 硬编码密钥到代码中有可能随代码泄露而暴露,有安全隐患,并不推荐。
// SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi
String secretId = tencentCloudProperties.getSecretId();
String secretKey = tencentCloudProperties.getSecretKey();
System.out.println("----- secretid = ");
System.out.println(secretId);
System.out.println(secretKey);
Credential cred = new Credential(secretId, secretKey);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
/* 非必要步骤:
* 实例化一个客户端配置对象,可以指定超时时间等配置 */
ClientProfile clientProfile = new ClientProfile();
/* SDK默认用TC3-HMAC-SHA256进行签名
* 非必要请不要修改这个字段 */
clientProfile.setSignMethod("HmacSHA256");
clientProfile.setHttpProfile(httpProfile);
/* 实例化要请求产品(以sms为例)的client对象
* 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
SmsClient client = new SmsClient(cred, "ap-guangzhou",clientProfile);
/* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
* 您可以直接查询SDK源码确定接口有哪些属性可以设置
* 属性可能是基本类型,也可能引用了另一个数据结构
* 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */
SendSmsRequest req = new SendSmsRequest();
/* 填充请求参数,这里request对象的成员变量即对应接口的入参
* 您可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义
* 基本类型的设置:
* 帮助链接:
* 短信控制台: https://console.cloud.tencent.com/smsv2
* 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */
// 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
String sdkAppId = "1400786711";
req.setSmsSdkAppId(sdkAppId);
/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
// 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
String signName = "荔枝的0321个人网";
req.setSignName(signName);
/* 模板 ID: 必须填写已审核通过的模板 ID */
// 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
String templateId = "1657722";
req.setTemplateId(templateId);
/* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */
String[] templateParamSet = {code};//{"1234"};
req.setTemplateParamSet(templateParamSet);
/* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
* 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */
String[] phoneNumberSet = {"+86" + phone};//电话号码//{"+8618616800164"};
req.setPhoneNumberSet(phoneNumberSet);
/* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
* 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
SendSmsResponse res = client.SendSms(req);
// 输出json格式的字符串回包
System.out.println(SendSmsResponse.toJsonString(res));
// 也可以取出单个值,您可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
// System.out.println(res.getRequestId());
/* 当出现以下错误码时,快速解决方案参考
* [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* 更多错误,可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
*/
} catch (TencentCloudSDKException e) {
System.out.println(e.toString());
}
}
// public static void main(String[] args) {
// try {
// new SMSUtils().sendSMS("18812345612", "7896");
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
}
通行证拦截器 短信发送频繁
@Slf4j
// 通行证拦截器
public class PassportInterceptor extends BaseInfoProperties implements HandlerInterceptor {
@Override //访问controller之前,拦截请求
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取用户Ip
/*
String userIp = IPUtil.getRequestIp(request);
String keyIp = MOBILE_SMSCODE + ":" + userIp;
*/
String mobile = request.getParameter("mobile");
String keyOnly = MOBILE_SMSCODE_ONLY+ ":" + mobile;
//得到是否存在的判断
boolean keyIsExist = redis.keyIsExist(keyOnly);
if (keyIsExist){
//异常捕获
GraceException.display(ResponseStatusEnum.SMS_NEED_WAIT_ERROR);
log.info("短信发送频率太大");
return false;
} else {
log.info(" redis 不存在 " + keyOnly);
}
/*
true :请求放行
false:请求拦截
*/
return true;
// return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override //访问controler之时,渲染视图之前
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override//访问controller ,渲染视图之后
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
异常捕获
- GraceException
/**
* 优雅的处理异常,统一封装
*/
public class GraceException {
public static void display(ResponseStatusEnum responseStatusEnum){
throw new MyCustomException(responseStatusEnum);
}
}
- MyCustomException
/**
* 自定义异常 ; 统一处理异常信息;便于解耦,可以在拦截器,控制层,业务层去使用
* 目的:统一处理异常信息
* 便于解耦,拦截器、service与controller 异常错误的解耦,
* 不会被service返回的类型而限制
*/
public class MyCustomException extends RuntimeException {
private ResponseStatusEnum responseStatusEnum;
public MyCustomException(ResponseStatusEnum responseStatusEnum) {
super("异常状态码为:" + responseStatusEnum.status()
+ ";具体异常信息为:" + responseStatusEnum.msg());
this.responseStatusEnum = responseStatusEnum;
}
public ResponseStatusEnum getResponseStatusEnum() {
return responseStatusEnum;
}
public void setResponseStatusEnum(ResponseStatusEnum responseStatusEnum) {
this.responseStatusEnum = responseStatusEnum;
}
}
- GraceExceptionHandler
@ControllerAdvice // 在Spring Boot中,可以使用@ControllerAdvice注解来创建全局异常处理类,以便统一处理不同控制器中抛出的异常。
public class GraceExceptionHandler {
// 自定义异常与拦截器整合返回json对象
@ExceptionHandler(MyCustomException.class)
@ResponseBody
public GraceJSONResult returnMyCustomException(MyCustomException e){
System.out.println("----补货到yichang");
// e.printStackTrace();
// return GraceJSONResult.errorMsg(e.getMessage());
return GraceJSONResult.exception(e.getResponseStatusEnum());
}
}
redis 项目中配置 application.yml
redis:
host: 127.0.0.1
port: 6379
database: 0
- 查看redis 信息
brew list
brew info redis