Commit e7255c67 authored by zhouxudong's avatar zhouxudong

添加白名单和 接口对称加密

parent 0a31652a
......@@ -169,6 +169,19 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>
<!--启用Spring Boot的配置处理器。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
......
package com.lyy.admin.VO.sms;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* @Author:zhouxudong
* @version: 1.0
* @Date: 2023/11/17 9:14
* @Description: 发送短信 vo
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@ApiModel(value = "发送短信")
public class SendPhoneVo {
@ApiModelProperty(value = "手机号",required = true)
private String phone;
//发送短信的类型 1:注册 2:登录
@ApiModelProperty(value = "短信类型 传数字 1:注册 2:登录 3:忘记密码 4:修改密码",required = true)
private Integer type;
@ApiModelProperty(value = "验证码信息不能为空,六位数字",required = true)
private Integer code;
}
package com.lyy.admin.VO.sms;
import lombok.*;
import java.util.List;
/**
* @Author:zhouxudong
* @version: 1.0
* @Date: 2023/11/20 14:38
* @Description: 短信返回格式
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class SmsVo {
private String code;
private String description;
private List<Object> result;
//返回样例
/* {
"result": [{
"total": 1,
"originTo": "18731081812",
"createTime": "2023-11-20T06:05:08Z",
"from": "8823112010314",
"smsMsgId": "0521b98d-75ae-467f-9672-8a9707095e67_28147916",
"countryId": "CN",
"status": "000000"
}],
"code": "000000",
"description": "Success"
}*/
}
package com.lyy.admin.common.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author:zhouxudong
* @version: 1.0
* @Date: 2023/12/12 9:40
* @Description: 解密注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.PARAMETER})
public @interface Decrypt {
}
package com.lyy.admin.common.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author:zhouxudong
* @version: 1.0
* @Date: 2023/12/12 9:40
* @Description: 加密注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
}
package com.lyy.admin.common.config;
import com.lyy.admin.common.constant.Constants;
import com.lyy.admin.common.interceptor.LoginInterceptor;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.concurrent.TimeUnit;
/**
* @author yaobaizheng
* @desc 解决跨域问题
......@@ -49,9 +44,7 @@ public class ResourceConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry)
{
if(lyyConfig.getEnableUrlFilter()){
String[] excludeSwagger = new String[]{"/swagger-resources/**",
"/webjars/**", "/v2/**","/v3/**", "/swagger-ui.html/**",
"/api", "/api-docs", "/api-docs/**", "/doc.html/**"};
String[] excludeSwagger = excludeSwagger();
String[] excludeUrl = new String[]{"/demo/login","/login/login","/weixin/login",
"/parkInfo/list",
"/developmentInfo/list","/megalopolisInfo/list",
......@@ -65,6 +58,26 @@ public class ResourceConfig implements WebMvcConfigurer {
}
}
private String[] excludeSwagger(){
String[] excludeSwagger = new String[]{"/swagger-resources/**",
"/swagger-ui.html",
"/webjars/**",
"/v3/**",
"/v2/**",
"/swagger**/**",
"/error",
"/swagger-ui/index.html",
"/swagger-ui/index.html/**",
"/doc.html",
"doc.html/**",
"/doc.html#/**",
"/**/login",
"/favicon.ico",
"/favicon.ico/**",
"/swagger-ui.html/**"};
return excludeSwagger;
}
/**
* 跨域配置
*/
......
package com.lyy.admin.common.config;
import lombok.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Author:zhouxudong
* @version: 1.0
* @Date: 2023/11/17 10:54
* @Description:
*/
@Component
@ConfigurationProperties(prefix = "sms")
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class SmsConfig {
private String appKey;
private String appSecret;
//app接入地址 //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
private String appUrl;
/** 签名通道号 //国内短信签名通道号*/
private String sender;
/** 短信签名 国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称*/
private String signature;
}
......@@ -117,16 +117,10 @@ public class Constants {
public static final int EXPIRE_TIME =5;
//秒
public static final int INTERVAL_TIME =4*60;
//#短信时间间隔(每天限制次数) 单位 天
public static final int intervalTime =1;
//短信每日发送次数
public static final int limitSms =5;
//------------------redis key--------------------------
public static final String token="Bearer ";
//手机验证码 规则: 手机验证码:验证码类型:手机号
public static final String MSG_KEY="phone:message:";
//用户查看数据详情 使用额度 map存储: 格式: key phone + ":" + type value
public static final String LIMIT_NUM="limit_num";
public static final String LIMIT_SMS="limit_sms:";
//---------------------end------------------------------
}
\ No newline at end of file
}
package com.lyy.admin.common.constant;
/**
* @Author:zhouxudong
* @version: 1.0
* @Date: 2023/12/1 11:55
* @Description: redis key
*/
public class RedisConstants {
//------------------redis key--------------------------
public static final String token="Token:";
//手机验证码 规则: 手机验证码:验证码类型:手机号
public static final String MSG_KEY="liyeren:phone:message:";
//用户查看数据详情 使用额度 map存储: 格式: key phone + ":" + type value
public static final String LIMIT_NUM="limit_num";
/**
* @description: 用于立业人发送短信 限制
* @date: 2023/12/11 18:35
* @param:
* @return:
**/
public static final String LIMIT_SMS="liyeren_limit_sms:";
//---------------------end------------------------------
}
package com.lyy.admin.common.encrypt;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONUtil;
import com.lyy.admin.common.anno.Decrypt;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* @Author:zhouxudong
*
* @version: 1.0 @Date: 2023/12/12 9:49 @Description:
*/
// @ControllerAdvice是一个全局数据处理组件
@ControllerAdvice
public class DecryptRequest extends RequestBodyAdviceAdapter {
@Value("${liyeren.key}")
private String decryptKey;
/**
* @description: 配置支持条件,只有方法或者参数有Decrypt注解的时候才生效
* @date: 2023/12/12 9:50
* @param: [methodParameter, targetType, converterType]
* @return: boolean
*/
@Override
public boolean supports(
MethodParameter methodParameter,
Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return methodParameter.hasMethodAnnotation(Decrypt.class)
|| methodParameter.hasParameterAnnotation(Decrypt.class);
}
/** 使用aop,在读取请求参数的之前,对请求参数进行解密 */
@Override
public HttpInputMessage beforeBodyRead(
HttpInputMessage inputMessage,
MethodParameter parameter,
Type targetType,
Class<? extends HttpMessageConverter<?>> converterType)
throws IOException {
// 获取密钥的字节
byte[] key = decryptKey.getBytes();
// 读取请求参数成为字节body
byte[] body = new byte[inputMessage.getBody().available()];
inputMessage.getBody().read(body);
String param = new String(body, StandardCharsets.UTF_8);
Map map = JSONUtil.toBean(JSONUtil.toJsonStr(param), Map.class);
System.out.println("body = " + new String(body, StandardCharsets.UTF_8));
try {
// 解密
AES aes = SecureUtil.aes(key);
// byte[] decrypt = aes.decrypt(body);
byte[] decrypt = aes.decrypt(Base64.decode(map.get("data").toString().getBytes()));
// 将解密的字节放入字节数组输入流中
ByteArrayInputStream bais = new ByteArrayInputStream(decrypt);
// 返回HttpInputMessage
return new HttpInputMessage() {
@Override
public InputStream getBody() {
return bais;
}
@Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
};
} catch (Exception e) {
e.printStackTrace();
}
// 如果不需要进行处理的话,则直接调用父类的beforeBodyRead,相当于直接返回inputMessage,不做处理
return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
}
}
package com.lyy.admin.common.encrypt;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lyy.admin.common.anno.Encrypt;
import com.lyy.admin.common.exception.LiyerenException;
import com.lyy.admin.common.utils.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @Author:zhouxudong
*
* @version: 1.0 @Date: 2023/12/12 10:07 @Description:
*/
@ControllerAdvice
@Slf4j
public class EncrptResponse implements ResponseBodyAdvice<Object> {
@Value("${liyeren.key}")
private String decryptKey;
/** 支持什么时候加密 */
@Override
public boolean supports(
MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return methodParameter.hasMethodAnnotation(Encrypt.class);
}
/** 数据响应进行加密 */
@Override
public Object beforeBodyWrite(
Object result,
MethodParameter methodParameter,
MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
AjaxResult value = AjaxResult.success(result);
log.info("result:{}", value);
boolean isStringResult = methodParameter.getParameterType().equals(String.class);
// 获取key的字节
AES aes = SecureUtil.aes(decryptKey.getBytes());
String info;
try {
if (isStringResult) {
ObjectMapper objectMapper = new ObjectMapper();
info = objectMapper.writeValueAsString(value);
return aes.encryptBase64(info);
}
// 如果msg和data存在的话,则进行加密,最后进行返回
return aes.encryptBase64(JSONUtil.toJsonStr(value));
} catch (Exception e) {
throw new LiyerenException("发生未知异常");
}
}
}
package com.lyy.admin.common.exception;
import lombok.Data;
@Data
public class LiyerenException extends RuntimeException {
private int code;
private String message;
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
public LiyerenException(int code, String message) {
this.code = code;
this.message = message;
}
public LiyerenException(String msg) {
this.code = 500;
this.message = msg;
}
public LiyerenException(APIExceptionEnum apiExceptionEnum) {
this.code = apiExceptionEnum.getCode();
this.message = apiExceptionEnum.getMessage();
}
}
......@@ -5,11 +5,16 @@ import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lyy.admin.common.exception.APIException;
import com.lyy.admin.common.exception.APIExceptionEnum;
import com.lyy.admin.common.exception.LiyerenException;
import com.lyy.admin.common.utils.IpUtils;
import com.lyy.admin.common.utils.StringUtils;
import com.lyy.admin.domain.SysUserToken;
import com.lyy.admin.mapper.SysUserInfoMapper;
import com.lyy.admin.mapper.SysUserTokenMapper;
import com.lyy.admin.mapper.system.SysMembershipInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
......@@ -18,6 +23,7 @@ import javax.servlet.http.HttpServletResponse;
import java.util.Date;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
......@@ -25,11 +31,26 @@ public class LoginInterceptor implements HandlerInterceptor {
@Autowired
SysMembershipInfoMapper sysMembershipInfoMapper;
@Value("${liyeren.whiteIp}")
private String whiteIp;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//白名单判断
String requestURI = request.getRequestURI();
log.info("请求地址:{}",requestURI);
if(requestURI.contains("/api/liyeren")){
//立业人请求接口
//查询白名单
String ip = IpUtils.getIpAddr(request);
String hostIp = IpUtils.getHostIp();
log.info("请求ip:{},主机ip:{}",ip,hostIp);
if(StringUtils.isEmpty(ip)||!whiteIp.contains(ip)){
throw new LiyerenException("没有请求权限");
}
return true;
}
String token = request.getHeader("token");// 从 http 请求头中取出 token
if(token == null || "".equals(token) ){
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@"+token);
......@@ -51,5 +72,4 @@ public class LoginInterceptor implements HandlerInterceptor {
return true;
}
}
package com.lyy.admin.common.utils;
import org.apache.commons.lang3.ObjectUtils;
import java.util.HashMap;
import java.util.Objects;
......@@ -179,9 +181,9 @@ public class AjaxResult extends HashMap<String, Object>
}
public static AjaxResult error(int code,String msg)
public static AjaxResult error(Integer code,String msg)
{
return new AjaxResult(ErrorCodeEum.ERROR.code(), msg, null);
return new AjaxResult(ObjectUtils.isEmpty(code) ?ErrorCodeEum.ERROR.code():code, msg, null);
}
/**
* 返回错误消息
......
package com.lyy.admin.common.utils;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lyy.admin.common.anno.Encrypt;
import com.lyy.admin.common.exception.APIException;
import com.lyy.admin.common.exception.LiyerenException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
......@@ -26,13 +33,15 @@ import javax.servlet.http.HttpServletRequest;
*/
@RestControllerAdvice(basePackages = "com.lyy.admin.controller")
public class GlobalExceptionHandler implements ResponseBodyAdvice {
@Value("${liyeren.key}")
private String decryptKey;
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
boolean assignableFrom = returnType.getParameterType().isAssignableFrom(AjaxResult.class);
return !assignableFrom;
boolean Encrypt = returnType.hasMethodAnnotation(Encrypt.class);
return !assignableFrom&&!Encrypt;
}
@Override
......@@ -64,6 +73,23 @@ public class GlobalExceptionHandler implements ResponseBodyAdvice {
return fail;
}
/*@ExceptionHandler(LiyerenException.class)
public Object APIExceptionHandler(LiyerenException apiException) {
int code = apiException.getCode();
String message = apiException.getMessage();
AjaxResult fail = AjaxResult.error( code,message);
AES aes = SecureUtil.aes(decryptKey.getBytes());
return aes.encryptBase64(JSONUtil.toJsonStr(fail));
}*/
@ExceptionHandler(LiyerenException.class)
public AjaxResult APIExceptionHandler(LiyerenException apiException) {
int code = apiException.getCode();
String message = apiException.getMessage();
AjaxResult fail = AjaxResult.error( code,message);
return fail;
}
/**
* 请求方式不支持
*/
......
package com.lyy.admin.common.utils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 获取IP方法
*
* @author ruoyi
*/
public class IpUtils
{
public static String getIpAddr(HttpServletRequest request)
{
if (request == null)
{
return null;
}
String ip = null;
// X-Forwarded-For:Squid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
// Proxy-Client-IP:apache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
// WL-Proxy-Client-IP:weblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
// HTTP_CLIENT_IP:有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
// X-Real-IP:nginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}
// 有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0)
{
ip = ipAddresses.split(",")[0];
}
// 还是不能获取到,最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
ip = request.getRemoteAddr();
}
return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
}
public static boolean internalIp(String ip)
{
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
private static boolean internalIp(byte[] addr)
{
if (StringUtils.isNull(addr) || addr.length < 2)
{
return true;
}
final byte b0 = addr[0];
final byte b1 = addr[1];
// 10.x.x.x/8
final byte SECTION_1 = 0x0A;
// 172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
// 192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0)
{
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4)
{
return true;
}
case SECTION_5:
switch (b1)
{
case SECTION_6:
return true;
}
default:
return false;
}
}
/**
* 将IPv4地址转换成字节
*
* @param text IPv4地址
* @return byte 字节
*/
public static byte[] textToNumericFormatV4(String text)
{
if (text.length() == 0)
{
return null;
}
byte[] bytes = new byte[4];
String[] elements = text.split("\\.", -1);
try
{
long l;
int i;
switch (elements.length)
{
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L)){
return null;
}
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L)) {
return null;
}
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 3:
for (i = 0; i < 2; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L)) {
return null;
}
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 4:
for (i = 0; i < 4; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
break;
default:
return null;
}
}
catch (NumberFormatException e)
{
return null;
}
return bytes;
}
public static String getHostIp()
{
try
{
return InetAddress.getLocalHost().getHostAddress();
}
catch (UnknownHostException e)
{
}
return "127.0.0.1";
}
public static String getHostName()
{
try
{
return InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException e)
{
}
return "未知";
}
}
......@@ -7,6 +7,7 @@ import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lyy.admin.VO.SysUserInfoVO;
import com.lyy.admin.common.constant.Constants;
import com.lyy.admin.common.constant.RedisConstants;
import com.lyy.admin.common.exception.APIException;
import com.lyy.admin.common.exception.APIExceptionEnum;
import com.lyy.admin.common.utils.BusinessService;
......@@ -75,11 +76,11 @@ public class WeixinController extends BaseController {
// 给当前用户存储 解锁次数
RedisUtil.hPut(
Constants.LIMIT_NUM,
RedisConstants.LIMIT_NUM,
phoneNumber + ":" + BusinessTypeEnum.PARK.getCode(),
BusinessTypeEnum.PARK.getNumber());
RedisUtil.hPut(
Constants.LIMIT_NUM,
RedisConstants.LIMIT_NUM,
phoneNumber + ":" + BusinessTypeEnum.CARRIER.getCode(),
BusinessTypeEnum.CARRIER.getNumber());
// 保存一条线索信息
......
package com.lyy.admin.controller.api;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import com.lyy.admin.VO.sms.SendPhoneVo;
import com.lyy.admin.common.anno.Decrypt;
import com.lyy.admin.common.anno.Encrypt;
import com.lyy.admin.service.liyerenapi.LiyerenService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author:zhouxudong
* @version: 1.0
* @Date: 2023/12/11 18:00
* @Description: 提供给立业人 app 接口
*/
@RestController
@RequestMapping("/api/liyeren")
@Api("立业人接口")
public class ApiController {
@Autowired
LiyerenService liyerenService;
//@Encrypt 加在方法上 @Decrypt 加在参数上
@PostMapping("/sendMsg")
@ApiOperation(value = "发送短信", notes = "发送短信")
public Boolean sendMsg(@RequestBody SendPhoneVo sendPhoneVo){
return this.liyerenService.sendMsg(sendPhoneVo);
}
}
package com.lyy.admin.domain.smslog;
import com.baomidou.mybatisplus.annotation.*;
import lombok.*;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 短信发送记录表;
*
* @author : http://www.chiner.pro
* @date : 2023-11-20
*/
@TableName("sys_sms_log")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Accessors(chain = true)
public class SysSmsLogEntity {
@TableId(type = IdType.ASSIGN_ID)
protected Long id;
/** 返回标识符 */
private String code;
/** 返回字段描述 */
private String description;
/** json格式字符串 */
private String result;
/** 短信类型 */
private String type;
private String phone;
protected String createBy;
protected LocalDateTime createTime;
protected String updateBy;
protected LocalDateTime updateTime;
protected Integer version;
}
package com.lyy.admin.enumerate;
import lombok.Getter;
/**
* @description: 立业人使用枚举
* @date: 2023/12/11 18:34
* @param:
* @return:
**/
@Getter
public enum SendMsgTypeEnum {
REGISTER(1, "a0072fe0c74c47e59fbf700a4ce94350", "立业人_注册"),
LOGIN(2, "a0072fe0c74c47e59fbf700a4ce94350", "立业人_登录"),
FORGOT_PASSWORD(3, "a0072fe0c74c47e59fbf700a4ce94350", "立业人_忘记密码"),
UPDATE_PASSWORD(4, "a0072fe0c74c47e59fbf700a4ce94350", "立业人_修改密码");
private final Integer code;
private final String templateId;
private final String name;
SendMsgTypeEnum(Integer code, String templateId, String name) {
this.code = code;
this.templateId = templateId;
this.name = name;
}
public static String getTemplateId(Integer code) {
for (SendMsgTypeEnum bt : values()) {
if (bt.code.equals(code)) {
return bt.templateId;
}
}
return null;
}
public static String getName(Integer code) {
for (SendMsgTypeEnum bt : values()) {
if (bt.code.equals(code)) {
return bt.name;
}
}
return null;
}
}
package com.lyy.admin.mapper.sms;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lyy.admin.domain.smslog.SysSmsLogEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* @author: zhouxudong
* @version: 1.0
* @createTime: 2023/11/20 15:02
* @description: 短信发送记录表
*/
@Mapper
public interface SysSmsLogMapper extends BaseMapper<SysSmsLogEntity> {}
package com.lyy.admin.service.liyerenapi;
import com.lyy.admin.VO.sms.SendPhoneVo;
/**
* @Author:zhouxudong
* @version: 1.0
* @Date: 2023/12/11 18:12
* @Description:
*/
public interface LiyerenService {
/**
* @description: 发送短信
* @date: 2023/12/11 18:13
* @param: [sendPhoneVo]
* @return: java.lang.String
**/
Boolean sendMsg(SendPhoneVo sendPhoneVo);
}
package com.lyy.admin.service.liyerenapi.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.json.JSONUtil;
import com.lyy.admin.VO.sms.SendPhoneVo;
import com.lyy.admin.VO.sms.SmsVo;
import com.lyy.admin.common.constant.Constants;
import com.lyy.admin.common.constant.RedisConstants;
import com.lyy.admin.common.exception.LiyerenException;
import com.lyy.admin.common.utils.redis.RedisUtil;
import com.lyy.admin.domain.smslog.SysSmsLogEntity;
import com.lyy.admin.enumerate.SendMsgTypeEnum;
import com.lyy.admin.service.liyerenapi.LiyerenService;
import com.lyy.admin.common.utils.SendMsgUtils;
import com.lyy.admin.service.sms.SysSmsLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @Author:zhouxudong
*
* @version: 1.0 @Date: 2023/12/11 18:12 @Description:
*/
@Service
@Slf4j
public class LiyerenServiceImpl implements LiyerenService {
@Autowired private SendMsgUtils sendMsgUtils;
@Autowired private SysSmsLogService sysSmsLogService;
/**
* @description: 手机号每日发送次数
* @date: 2023/11/27 15:32
* @param: [phone]
* @return: void
*/
private void limitSms(String phone) {
String key = RedisConstants.LIMIT_SMS + phone;
DateTime now = DateUtil.date();
DateTime startTime = DateUtil.offsetDay(now, Constants.intervalTime * -1);
RedisUtil.delZset(key, 0, startTime.getTime());
Long count = RedisUtil.countZset(key, startTime.getTime(), now.getTime());
if (count == null || count <= Constants.limitSms) {
RedisUtil.addZset(key, now.getTime(), now.getTime());
} else {
throw new LiyerenException("短信发送频繁,每日短信发送次数为" + Constants.limitSms);
}
}
private void check(SendPhoneVo sendPhoneVo) {
if (StringUtils.isEmpty(sendPhoneVo.getPhone())) {
throw new LiyerenException("手机号不能为空");
}
boolean match = ReUtil.isMatch("^1[3-9]\\d{9}$", sendPhoneVo.getPhone());
if (!match) {
throw new LiyerenException("手机号码格式有误");
}
if (ObjectUtils.isEmpty(sendPhoneVo.getCode()) || ObjectUtils.isEmpty(sendPhoneVo.getType())) {
throw new LiyerenException("缺少参数");
}
}
/**
* @description: 发送短信
* @date: 2023/12/11 18:13
* @param: [sendPhoneVo]
* @return: java.lang.String
*/
@Override
public Boolean sendMsg(SendPhoneVo sendPhoneVo) {
check(sendPhoneVo);
if (StringUtils.isEmpty(SendMsgTypeEnum.getTemplateId(sendPhoneVo.getType()))) {
throw new LiyerenException("参数有误");
}
limitSms(sendPhoneVo.getPhone());
String key = RedisConstants.MSG_KEY + sendPhoneVo.getType() + ":" + sendPhoneVo.getPhone();
//
Long expire = RedisUtil.getExpire(key);
// 间隔 1分钟内
if (expire != null && expire > Constants.INTERVAL_TIME) {
throw new LiyerenException("短信已发送,请稍后重试");
}
List<Integer> temp = new ArrayList<>();
temp.add(sendPhoneVo.getCode());
/* // 发送短信
String result =
sendMsgUtils.sendMsg(
sendPhoneVo.getPhone(),
SendMsgTypeEnum.getTemplateId(sendPhoneVo.getType()),
temp.toString());
SmsVo smsVo = JSONUtil.toBean(result, SmsVo.class);
if (ObjectUtils.isEmpty(smsVo)) {
throw new LiyerenException("短信发送失败");
}
log.info("发送短信信息:{},返回结果:{}", sendPhoneVo.getCode(), result);
SysSmsLogEntity sysSmsLog = new SysSmsLogEntity();
sysSmsLog
.setCode(smsVo.getCode())
.setDescription(smsVo.getDescription())
.setType(SendMsgTypeEnum.getName(sendPhoneVo.getType()))
.setResult(result)
.setPhone(sendPhoneVo.getPhone());
this.sysSmsLogService.save(sysSmsLog);
if (!"000000".equals(smsVo.getCode())) {
log.error("短信发送失败:{},手机号:{}", smsVo, sendPhoneVo.getPhone());
throw new LiyerenException(407, result);
}*/
RedisUtil.set(
key, String.valueOf(sendPhoneVo.getCode()), Constants.EXPIRE_TIME, TimeUnit.MINUTES);
return true;
}
}
package com.lyy.admin.service.sms;
import java.util.List;
public interface IMessage {
boolean sendMsg(String messageContent,String phone);
boolean sendMsgBatch(String messageContent, List<String> phone);
}
package com.lyy.admin.service.sms;
import com.baomidou.mybatisplus.extension.service.IService;
import com.lyy.admin.domain.smslog.SysSmsLogEntity;
/**
* @author: zhouxudong
* @version: 1.0
* @createTime: 2023/11/20 15:01
* @description: 短信发送记录表
*/
public interface SysSmsLogService extends IService<SysSmsLogEntity> {
}
package com.lyy.admin.service.sms.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lyy.admin.domain.smslog.SysSmsLogEntity;
import com.lyy.admin.mapper.sms.SysSmsLogMapper;
import com.lyy.admin.service.sms.SysSmsLogService;
import org.springframework.stereotype.Service;
/**
* @author: zhouxudong
* @version: 1.0
* @createTime: 2023/11/20 15:01
* @description: 短信发送记录表
*/
@Service
public class SysSmsLogServiceImpl extends ServiceImpl<SysSmsLogMapper, SysSmsLogEntity> implements SysSmsLogService {}
......@@ -87,4 +87,18 @@ swagger:
# * 2、在方法上加@ApiOperationSupport(ignoreParameters = {"PCode"})
#*/
knife4j:
enable: true
\ No newline at end of file
enable: true
sms:
appKey: J12997I0SJ988a4Hjmk72WmuP51i
appSecret: I6bUKWfZU51i9FSsXGGg9XkN2GRw
appUrl: https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1
sender: 8823112010314
signature: 立业云
liyeren:
whiteIp: 192.168.192.122
#立业人加密密钥
key: a5gcCE1MV4j2W9x7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lyy.user.moudle.smslog.mapper.SysSmsLogMapper">
</mapper>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment