perf: 优化参数存储安全问题

1. 加密参数配置数据
2. 优化手机验证码为读取加密参数配置
pull/19/head
Parker 3 years ago
parent edd6dcec59
commit e8f9f15b39

@ -149,7 +149,7 @@ public interface OptionsApi {
* @param optionCode
* @return ResultWrapper
*/
@GetMapping("/getByCode")
//@GetMapping("/getByCode")
ResultWrapper<OptionsModel> getByCode(String optionCode);
/**
@ -163,7 +163,7 @@ public interface OptionsApi {
*
* @return ResultWrapper
*/
@GetMapping("/findAll")
//@GetMapping("/findAll")
ResultWrapper<List<OptionsModel>> findAll();
/**

@ -57,21 +57,21 @@ public class OptionsModel extends ApiWrapper {
/** 参数值 */
@ApiModelProperty(value = "参数值")
@ExcelProperty(value = "参数值", order = 3)
@ExcelProperty(value = "参数值", order = 4)
@ExcelInfo
@ValidatorLenMax(10000)
private String optionValue;
/** 是否内置数据 0否 1是*/
@ApiModelProperty(value = "是否内置数据 0否 1是")
@ExcelProperty(value = "是否内置数据", order = 4)
@ExcelProperty(value = "是否内置数据", order = 5)
@ExcelInfo(dictType = "no_yes")
@ValidatorLenMax(1)
private String izLock;
/** 备注 */
@ApiModelProperty(value = "备注")
@ExcelProperty(value = "备注", order = 5)
@ExcelProperty(value = "备注", order = 6)
@ExcelInfo
@ValidatorLenMax(255)
private String remark;

@ -18,6 +18,7 @@ package org.opsli.core.autoconfigure.properties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
@ -26,7 +27,7 @@ import org.springframework.stereotype.Component;
* @author Parker
* @date 2020-09-15
*/
@Component
@Configuration
@ConfigurationProperties(prefix = ApiPathProperties.PROP_PREFIX)
@Data
@EqualsAndHashCode(callSuper = false)

@ -18,6 +18,7 @@ package org.opsli.core.autoconfigure.properties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
@ -26,7 +27,7 @@ import org.springframework.stereotype.Component;
* @author Parker
* @date 2020-09-15
*/
@Component
@Configuration
@ConfigurationProperties(prefix = CacheProperties.PROP_PREFIX)
@Data
@EqualsAndHashCode(callSuper = false)

@ -0,0 +1,25 @@
package org.opsli.core.autoconfigure.properties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
*
*
* @author Parker
* @date 2022-08-07
*/
@Configuration
@ConfigurationProperties(prefix = EncryptProperties.PROP_PREFIX)
@Data
@EqualsAndHashCode(callSuper = false)
public class EncryptProperties {
public static final String PROP_PREFIX = "opsli.data-encrypt";
/** 秘钥 */
private String key;
}

@ -18,6 +18,7 @@ package org.opsli.core.autoconfigure.properties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
@ -26,7 +27,7 @@ import org.springframework.stereotype.Component;
* @author Parker
* @date 2020-09-15
*/
@Component
@Configuration
@ConfigurationProperties(prefix = TokenProperties.PROP_PREFIX)
@Data
@EqualsAndHashCode(callSuper = false)

@ -0,0 +1,336 @@
package org.opsli.core.filters.interceptor;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import opsli.plugins.crypto.spring.annotation.CryptoMapperField;
import opsli.plugins.crypto.spring.crypto.ICrypto;
import opsli.plugins.crypto.spring.enums.CryptoType;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.opsli.core.autoconfigure.properties.EncryptProperties;
import org.springframework.stereotype.Component;
import java.lang.reflect.*;
import java.time.chrono.ChronoLocalDate;
import java.util.*;
/**
* Mybatis
* <p>
* Signature:
* Signature.type: ExecutorParameterHandlerStatementHandlerResultSetHandler
* Signature.method:
* Signature.args:JAVA
*
* @author Parker
* @date 2022-08-07
*/
@Slf4j
@AllArgsConstructor
@Component
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
}
)
public class MybatisCryptoInterceptor implements Interceptor {
private final EncryptProperties encryptProperties;
/**
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
Method method = invocation.getMethod();
switch (method.getName()) {
case "update":
return updateHandle(invocation);
case "query":
return selectHandle(invocation);
default:
return invocation.proceed();
}
}
/**
*
*
* @param invocation
* @return
* @throws Throwable
*/
private Object selectHandle(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler<Object> resultHandler = (ResultHandler) args[3];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
//处理参数作为条件查询需要加密
handleParameterOrResult(parameter, CryptoType.ENCRYPT);
//由于逻辑关系,只会进入一次
if (args.length == 4) {
//4 个参数时
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 个参数时
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
List<Object> resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
for (Object o : resultList) {
handleParameterOrResult(o, CryptoType.DECRYPT);
}
return resultList;
}
/**
*
*
* @param invocation
* @return
* @throws Throwable
*/
private Object updateHandle(Invocation invocation) throws Throwable {
//处理参数
handleParameterOrResult(invocation.getArgs()[1], CryptoType.ENCRYPT);
return invocation.proceed();
}
/**
*
*
* @param object
* @param cryptoType
* @throws IllegalAccessException
*/
private void handleParameterOrResult(Object object, CryptoType cryptoType) throws IllegalAccessException {
HashMap<Field, Object> fieldObjectHashMap = new HashMap<>();
//多个参数
if (object instanceof Map) {
Map paramMap = (Map) object;
Set keySet = paramMap.keySet();
for (Object key : keySet) {
Object o = paramMap.get(key);
if (o != null) {
handleObject(o, o.getClass(), fieldObjectHashMap);
}
}
} else {
if (object != null) {
handleObject(object, object.getClass(), fieldObjectHashMap);
}
}
//统一修改加密解密值
fieldObjectHashMap.keySet().forEach(key -> {
try {
handleString(key, fieldObjectHashMap.get(key), cryptoType);
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
*
*
* @param type
* @return
*/
private boolean isBase(Type type) {
return boolean.class.equals(type) ||
char.class.equals(type) ||
long.class.equals(type) ||
int.class.equals(type) ||
byte.class.equals(type) ||
short.class.equals(type) ||
double.class.equals(type) ||
float.class.equals(type);
}
/**
*
*
* @param object
* @return
*/
private boolean isFilter(Object object) {
return object == null || object instanceof CharSequence || object instanceof Number || object instanceof Collection || object instanceof Date || object instanceof ChronoLocalDate;
}
/**
*
*
* @param oClass
* @param fields
* @return
*/
private List<Field> mergeField(Class<?> oClass, List<Field> fields) {
if (fields == null) {
fields = new ArrayList<>();
}
Class<?> superclass = oClass.getSuperclass();
if (superclass != null && !superclass.equals(Object.class) && superclass.getDeclaredFields().length > 0) {
mergeField(superclass, fields);
}
for (Field declaredField : oClass.getDeclaredFields()) {
int modifiers = declaredField.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers) || Modifier.isVolatile(modifiers) || Modifier.isSynchronized(modifiers)) {
continue;
}
fields.add(declaredField);
}
return fields;
}
/**
* Object
*
* @param obj
* @param oClass
* @throws IllegalAccessException
*/
private void handleObject(Object obj, Class<?> oClass, HashMap<Field, Object> fieldObjectHashMap) throws IllegalAccessException {
//过滤
if (isFilter(obj)) {
return;
}
List<Field> fields = mergeField(oClass, null);
for (Field declaredField : fields) {
//静态属性直接跳过
if (Modifier.isStatic(declaredField.getModifiers())) {
continue;
}
boolean accessible = declaredField.isAccessible();
declaredField.setAccessible(true);
Object value = declaredField.get(obj);
declaredField.setAccessible(accessible);
if (value == null) {
// TODO
continue;
} else if (value instanceof Number) {
// TODO
continue;
} else if (value instanceof String) {
CryptoMapperField annotation = declaredField.getAnnotation(CryptoMapperField.class);
if (annotation != null) {
fieldObjectHashMap.put(declaredField, obj);
}
} else if (value instanceof Collection) {
Collection coll = (Collection) value;
for (Object o : coll) {
if (isFilter(o)) {
//默认集合内类型一致
break;
}
handleObject(o, o.getClass(), fieldObjectHashMap);
}
} else {
handleObject(value, value.getClass(), fieldObjectHashMap);
}
}
}
/**
*
*
* @param field
* @param object
* @param cryptoType
* @throws IllegalAccessException
* @throws InstantiationException
* @throws NoSuchMethodException
* @throws InvocationTargetException
*/
private void handleString(Field field, Object object, CryptoType cryptoType) throws Exception {
boolean accessible = field.isAccessible();
field.setAccessible(true);
Object value = field.get(object);
CryptoMapperField annotation = field.getAnnotation(CryptoMapperField.class);
if (annotation != null) {
String key;
//全局配置的key
String propertiesKey = encryptProperties.getKey();
log.debug("全局key是" + propertiesKey);
//属性上的key
String annotationKey = annotation.key();
log.debug("注解key是" + annotationKey);
if (StrUtil.isNotBlank(annotationKey)) {
key = annotationKey;
} else {
key = propertiesKey;
}
Class<? extends ICrypto> iCryptoImpl = annotation.iCrypto();
ICrypto iCrypto = iCryptoImpl.newInstance();
//解密后的值
String valueResult;
if (cryptoType.equals(CryptoType.DECRYPT)) {
valueResult = iCrypto.decrypt(String.valueOf(value), key);
} else {
valueResult = iCrypto.encrypt(String.valueOf(value), key);
}
log.debug("原值:" + value);
log.debug("现在:" + valueResult);
field.set(object, String.valueOf(valueResult));
field.setAccessible(accessible);
}
}
/**
*
*
* @param target
* @return
*/
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}

@ -0,0 +1,81 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.options;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.opsli.common.annotation.OptionDict;
import org.opsli.common.annotation.validator.Validator;
import org.opsli.common.enums.ValidatorType;
import org.opsli.core.utils.OptionsUtil;
import org.opsli.core.utils.ValidatorUtil;
import java.io.Serializable;
/**
* -
*
* @author Parker
* @date 2020-09-19 20:03
*/
public enum SmsAliYunCaptchaConfigFactory {
/** 实例对象 */
INSTANCE;
/**
*
* @return LocalConfig
*/
public SmsAliYunCaptchaConfigOption getConfig() {
SmsAliYunCaptchaConfigOption option = new SmsAliYunCaptchaConfigOption();
// 获得缓存参数配置
OptionsUtil.getOptionByBean(option);
// 验证配置
ValidatorUtil.verify(option);
// 转化对象
return option;
}
// =======================
/**
*
*
* @author Parker
*/
@Data
public static class SmsAliYunCaptchaConfigOption implements Serializable {
private static final long serialVersionUID = 1L;
/** 模版编码 */
@ApiModelProperty(value = "模版编码")
@Validator({ValidatorType.IS_NOT_NULL})
@OptionDict("sms_aliyun_captcha_template_code")
private String templateCode;
/** 签名 */
@ApiModelProperty(value = "签名")
@Validator({ValidatorType.IS_NOT_NULL})
@OptionDict("sms_aliyun_captcha_sign")
private String sign;
}
}

@ -0,0 +1,81 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.options;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.opsli.common.annotation.OptionDict;
import org.opsli.common.annotation.validator.Validator;
import org.opsli.common.enums.ValidatorType;
import org.opsli.core.utils.OptionsUtil;
import org.opsli.core.utils.ValidatorUtil;
import java.io.Serializable;
/**
*
*
* @author Parker
* @date 2020-09-19 20:03
*/
public enum SmsAliYunConfigFactory {
/** 实例对象 */
INSTANCE;
/**
*
* @return LocalConfig
*/
public SmsAliYunConfigOption getConfig() {
SmsAliYunConfigOption option = new SmsAliYunConfigOption();
// 获得缓存参数配置
OptionsUtil.getOptionByBean(option);
// 验证配置
ValidatorUtil.verify(option);
// 转化对象
return option;
}
// =======================
/**
*
*
* @author Parker
*/
@Data
public static class SmsAliYunConfigOption implements Serializable {
private static final long serialVersionUID = 1L;
/** access_key */
@ApiModelProperty(value = "阿里云AccessKey")
@Validator({ValidatorType.IS_NOT_NULL})
@OptionDict("sms_aliyun_access_key")
private String accessKey;
/** access_key_secret */
@ApiModelProperty(value = "阿里云AccessKeySecret")
@Validator({ValidatorType.IS_NOT_NULL})
@OptionDict("sms_aliyun_access_key_secret")
private String accessKeySecret;
}
}

@ -23,6 +23,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import lombok.EqualsAndHashCode;
import opsli.plugins.crypto.spring.annotation.CryptoMapperField;
import org.opsli.core.base.entity.BaseEntity;
/**
@ -42,12 +43,16 @@ public class SysOptions extends BaseEntity {
private String optionName;
/** 参数值 */
@CryptoMapperField
@TableField(updateStrategy = FieldStrategy.IGNORED)
private String optionValue;
/** 是否内置数据 0否 1是*/
private String izLock;
/** 是否忽略 0否 1是*/
private String izExclude;
/** 备注 */
@TableField(updateStrategy = FieldStrategy.IGNORED)
private String remark;

@ -19,6 +19,7 @@ package org.opsli.modulars.system.options.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@ -39,6 +40,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -146,53 +148,49 @@ public class SysOptionsServiceImpl extends CrudServiceImpl<SysOptionsMapper, Sys
throw new ServiceException(SystemMsg.EXCEPTION_OPTIONS_UPDATE);
}
List<String> optionsCode = Lists.newArrayList();
optionsCode.addAll(params.keySet());
List<String> optionsCodeList = Lists.newArrayList();
optionsCodeList.addAll(params.keySet());
// 获得所有的原始内容
QueryWrapper<SysOptions> queryWrapper = new QueryWrapper<>();
queryWrapper.in("option_code", optionsCode);
queryWrapper.in("option_code", optionsCodeList);
List<SysOptions> optionsList = this.findList(queryWrapper);
Map<String, OptionsModel> resourceDataDict = Maps.newHashMap();
Map<String, SysOptions> resourceDataDict = Maps.newHashMap();
for (SysOptions option : optionsList) {
resourceDataDict.put(option.getOptionCode(),
transformT2M(option)
);
resourceDataDict.put(option.getOptionCode(), option);
}
List<OptionsModel> updateOptionsModelList = new ArrayList<>();
// 循环修改
for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
OptionsModel optionsModel = resourceDataDict.get(key);
if(optionsModel == null){
optionsModel = new OptionsModel();
// 新增参数
// 设置值
optionsModel.setIzApi(true);
optionsModel.setOptionCode(key);
optionsModel.setOptionValue(value);
optionsModel.setVersion(0);
// 强制设置为内置数据
optionsModel.setIzLock(DictType.NO_YES_YES.getValue());
// 更新
this.insert(optionsModel);
}else {
// 修改参数
// 设置值
optionsModel.setIzApi(true);
optionsModel.setOptionValue(value);
// 强制设置为内置数据
optionsModel.setIzLock(DictType.NO_YES_YES.getValue());
// 更新
this.update(optionsModel);
SysOptions options = resourceDataDict.get(key);
if(null == options){
options = new SysOptions();
}
// 如果是 排除字段 且 传入参数为空 则 不处理
if(DictType.NO_YES_YES.getValue().equals(options.getIzExclude())
&& StrUtil.isBlank(value)){
continue;
}
// 设置值
options.setIzApi(true);
options.setOptionValue(value);
// 强制设置为内置数据
options.setIzLock(DictType.NO_YES_YES.getValue());
// 更新
this.saveOrUpdate(options);
updateOptionsModelList.add(transformT2M(options));
}
// 清除缓存
this.clearCache(updateOptionsModelList);
}
@Override

@ -267,7 +267,7 @@ public class SysOptionsRestController extends BaseRestController<SysOptions, Opt
}
/**
*
*
* @return ResultWrapper
*/
@Override
@ -275,8 +275,12 @@ public class SysOptionsRestController extends BaseRestController<SysOptions, Opt
QueryWrapper<SysOptions> queryWrapper = new QueryWrapper<>();
// 查询内置数据
queryWrapper.eq("iz_lock", DictType.NO_YES_YES.getValue());
// 查询不忽略的数据
queryWrapper.eq("iz_exclude", DictType.NO_YES_NO.getValue());
// 数据库查询数据
List<SysOptions> allList = IService.findList(queryWrapper);
return ResultWrapper.getSuccessResultWrapper(
OptionsUtil.convertOptionsMap(
WrapperUtil.transformInstance(allList, OptionsModel.class))

@ -21,6 +21,8 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.opsli.core.autoconfigure.properties.GlobalProperties;
import org.opsli.core.options.EmailConfigFactory;
import org.opsli.core.options.SmsAliYunCaptchaConfigFactory;
import org.opsli.core.options.SmsAliYunConfigFactory;
import org.opsli.core.utils.EnjoyUtil;
import org.opsli.core.utils.VerificationCodeUtil;
import org.opsli.plugins.email.EmailPlugin;
@ -43,11 +45,6 @@ import java.util.Map;
@Component
public class VerificationCodeBean {
private static final String EMAIL_FTL = "/email/email_code.ftl";
/** TODO 短信签名 请修改 */
private static final String SMS_SIGN_NAME = "阿里云短信测试";
/** TODO 短信模版编号 请修改 */
private static final String SMS_TEMPLATE_CODE = "SMS_154950909";
private final EmailPlugin emailPlugin;
private final GlobalProperties globalProperties;
@ -81,11 +78,19 @@ public class VerificationCodeBean {
VerificationCodeUtil.VerificationCodeModel verificationCodeModel =
VerificationCodeUtil.createMobileCode(mobile, type);
SmsAliYunConfigFactory.SmsAliYunConfigOption aliYunConfigOption =
SmsAliYunConfigFactory.INSTANCE.getConfig();
SmsAliYunCaptchaConfigFactory.SmsAliYunCaptchaConfigOption aliYunCaptchaConfigOption =
SmsAliYunCaptchaConfigFactory.INSTANCE.getConfig();
Map<String, String> templateParam = MapUtil.newHashMap(1);
templateParam.put("code", verificationCodeModel.getVerificationCode());
SmsModel smsModel = SmsModel.builder()
.signName(SMS_SIGN_NAME)
.templateCode(SMS_TEMPLATE_CODE)
.accessKey(aliYunConfigOption.getAccessKey())
.accessKeySecret(aliYunConfigOption.getAccessKeySecret())
.signName(aliYunCaptchaConfigOption.getSign())
.templateCode(aliYunCaptchaConfigOption.getTemplateCode())
.templateParam(templateParam)
.tels(Collections.singletonList(mobile))
.build();

@ -15,7 +15,10 @@
*/
package opsli.plugins.crypto.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import opsli.plugins.crypto.enums.CryptoSymmetricType;
/**
@ -25,6 +28,9 @@ import opsli.plugins.crypto.enums.CryptoSymmetricType;
* @date 202151715:59:52
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CryptoSymmetric {
/** 加解密类别 */

@ -0,0 +1,36 @@
package opsli.plugins.crypto.spring.annotation;
import opsli.plugins.crypto.spring.crypto.ICrypto;
import opsli.plugins.crypto.spring.crypto.impl.AESCrypto;
import java.lang.annotation.*;
/**
*
* mybatis Interceptor
*
* @author Parker
* @date 2022-08-07 17:33
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface CryptoMapperField {
/**
* ()
* encrypt.property.key
* @return String
*/
String key() default "0546c7b2790448658a1816a7803d7ba1";
/**
*
* @return Class<? extends ICrypto>
*/
Class<? extends ICrypto> iCrypto() default AESCrypto.class;
}

@ -0,0 +1,29 @@
package opsli.plugins.crypto.spring.crypto;
/**
*
*
* @author zhangylo
*/
public interface ICrypto {
/**
*
*
* @param value
* @param key
* @return
* @throws Exception
*/
String encrypt(String value, String key) throws Exception;
/**
*
*
* @param value
* @param key
* @return
* @throws Exception
*/
String decrypt(String value, String key) throws Exception;
}

@ -0,0 +1,53 @@
package opsli.plugins.crypto.spring.crypto.impl;
import lombok.extern.slf4j.Slf4j;
import opsli.plugins.crypto.CryptoPlugin;
import opsli.plugins.crypto.enums.CryptoSymmetricType;
import opsli.plugins.crypto.model.CryptoSymmetric;
import opsli.plugins.crypto.spring.crypto.ICrypto;
import opsli.plugins.crypto.strategy.CryptoSymmetricService;
/**
* AES
*
* @author Parker
* @date 2022-08-07 17:33
*/
@Slf4j
public class AESCrypto implements ICrypto {
/**
*
*
* @param value
* @param key
* @return
*/
@Override
public String encrypt(String value, String key) throws Exception {
CryptoSymmetric cryptoSymmetric = CryptoSymmetric.builder()
.cryptoType(CryptoSymmetricType.AES)
.privateKey(key)
.build();
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
return symmetric.encrypt(cryptoSymmetric, value);
}
/**
*
*
* @param value
* @param key
* @return
*/
@Override
public String decrypt(String value, String key) {
CryptoSymmetric cryptoSymmetric = CryptoSymmetric.builder()
.cryptoType(CryptoSymmetricType.AES)
.privateKey(key)
.build();
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
return symmetric.decrypt(cryptoSymmetric, value);
}
}

@ -0,0 +1,53 @@
package opsli.plugins.crypto.spring.crypto.impl;
import lombok.extern.slf4j.Slf4j;
import opsli.plugins.crypto.CryptoPlugin;
import opsli.plugins.crypto.enums.CryptoSymmetricType;
import opsli.plugins.crypto.model.CryptoSymmetric;
import opsli.plugins.crypto.spring.crypto.ICrypto;
import opsli.plugins.crypto.strategy.CryptoSymmetricService;
/**
* DES
*
* @author Parker
* @date 2022-08-07 17:33
*/
@Slf4j
public class DESCrypto implements ICrypto {
/**
*
*
* @param value
* @param key
* @return
*/
@Override
public String encrypt(String value, String key) throws Exception {
CryptoSymmetric cryptoSymmetric = CryptoSymmetric.builder()
.cryptoType(CryptoSymmetricType.DES)
.privateKey(key)
.build();
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
return symmetric.encrypt(cryptoSymmetric, value);
}
/**
*
*
* @param value
* @param key
* @return
*/
@Override
public String decrypt(String value, String key) {
CryptoSymmetric cryptoSymmetric = CryptoSymmetric.builder()
.cryptoType(CryptoSymmetricType.DES)
.privateKey(key)
.build();
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
return symmetric.decrypt(cryptoSymmetric, value);
}
}

@ -0,0 +1,53 @@
package opsli.plugins.crypto.spring.crypto.impl;
import lombok.extern.slf4j.Slf4j;
import opsli.plugins.crypto.CryptoPlugin;
import opsli.plugins.crypto.enums.CryptoSymmetricType;
import opsli.plugins.crypto.model.CryptoSymmetric;
import opsli.plugins.crypto.spring.crypto.ICrypto;
import opsli.plugins.crypto.strategy.CryptoSymmetricService;
/**
* DES
*
* @author Parker
* @date 2022-08-07 17:33
*/
@Slf4j
public class DESEDECrypto implements ICrypto {
/**
*
*
* @param value
* @param key
* @return
*/
@Override
public String encrypt(String value, String key) throws Exception {
CryptoSymmetric cryptoSymmetric = CryptoSymmetric.builder()
.cryptoType(CryptoSymmetricType.DE_SEDE)
.privateKey(key)
.build();
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
return symmetric.encrypt(cryptoSymmetric, value);
}
/**
*
*
* @param value
* @param key
* @return
*/
@Override
public String decrypt(String value, String key) {
CryptoSymmetric cryptoSymmetric = CryptoSymmetric.builder()
.cryptoType(CryptoSymmetricType.DE_SEDE)
.privateKey(key)
.build();
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
return symmetric.decrypt(cryptoSymmetric, value);
}
}

@ -0,0 +1,53 @@
package opsli.plugins.crypto.spring.crypto.impl;
import lombok.extern.slf4j.Slf4j;
import opsli.plugins.crypto.CryptoPlugin;
import opsli.plugins.crypto.enums.CryptoSymmetricType;
import opsli.plugins.crypto.model.CryptoSymmetric;
import opsli.plugins.crypto.spring.crypto.ICrypto;
import opsli.plugins.crypto.strategy.CryptoSymmetricService;
/**
* 4
*
* @author Parker
* @date 2022-08-07 17:33
*/
@Slf4j
public class SM4Crypto implements ICrypto {
/**
*
*
* @param value
* @param key
* @return
*/
@Override
public String encrypt(String value, String key) throws Exception {
CryptoSymmetric cryptoSymmetric = CryptoSymmetric.builder()
.cryptoType(CryptoSymmetricType.SM4)
.privateKey(key)
.build();
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
return symmetric.encrypt(cryptoSymmetric, value);
}
/**
*
*
* @param value
* @param key
* @return
*/
@Override
public String decrypt(String value, String key) {
CryptoSymmetric cryptoSymmetric = CryptoSymmetric.builder()
.cryptoType(CryptoSymmetricType.SM4)
.privateKey(key)
.build();
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
return symmetric.decrypt(cryptoSymmetric, value);
}
}

@ -0,0 +1,32 @@
package opsli.plugins.crypto.spring.enums;
/**
*
*
* @author zhangylo
*/
public enum CryptoType {
/**
* ENCRYPT
* DECRYPT
*/
ENCRYPT("com/encrypt"), DECRYPT("decrypt");
/**
*
*/
private String method;
CryptoType(String method) {
this.method = method;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}

@ -1,43 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.plugins.sms.conf;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
*
*
* @author yanxiaojian
* @date 2021-06-10
**/
@Data
@EqualsAndHashCode(callSuper = false)
@Component
@ConfigurationProperties(prefix = AliYunSmsProperties.PROP_PREFIX)
public class AliYunSmsProperties {
public static final String PROP_PREFIX = "sms.aliyun";
/** 主账号id */
private String accessKeyId;
/** 密钥 */
private String accessKeySecret;
}

@ -38,6 +38,12 @@ public class SmsModel implements Serializable {
private static final long serialVersionUID = 1L;
/** access_key */
private String accessKey;
/** access_key_secret */
private String accessKeySecret;
/** 手机号集合 */
List<String> tels;

@ -44,6 +44,8 @@ public enum SmsMsgCodeEnum implements BaseMsg {
CODE_ERROR_SMS_TEMPLATE_CODE_NULL(310002, "模版编号不可为空"),
CODE_ERROR_SMS_TEMPLATE_PARAM_NULL(310003, "模版参数不可为空"),
CODE_ERROR_SMS_PHONE_NUMBERS_NULL(310004, "手机号不可为空"),
CODE_ERROR_SMS_ACCESS_KEY_NULL(310001, "ACCESS_KEY 不可为空"),
CODE_ERROR_SMS_ACCESS_KEY_SECRET_NULL(310001, "ACCESS_KEY_SECRET 不可为空"),
CODE_ERROR_SMS_INIT(310010, "初始化参数异常"),
EXCEPTION_IS_PHONE(98010,"不是座机号码+手机号码CharUtil中国+ 400 + 800电话 + 手机号号码(香港)! "),

@ -21,13 +21,11 @@ import cn.hutool.core.util.StrUtil;
import cn.javaer.aliyun.sms.SmsClient;
import cn.javaer.aliyun.sms.SmsTemplate;
import lombok.extern.slf4j.Slf4j;
import org.opsli.plugins.sms.conf.AliYunSmsProperties;
import org.opsli.plugins.sms.enums.SmsType;
import org.opsli.plugins.sms.exceptions.SmsException;
import org.opsli.plugins.sms.model.SmsModel;
import org.opsli.plugins.sms.msg.SmsMsgCodeEnum;
import org.opsli.plugins.sms.service.SmsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@ -43,8 +41,6 @@ import java.util.Map;
@Service
public class AliYunSmsServiceImpl implements SmsService {
@Autowired
private AliYunSmsProperties aliyunSmsProperties;
@Override
public SmsType getType() {
@ -54,28 +50,23 @@ public class AliYunSmsServiceImpl implements SmsService {
@Override
public void sendSms(SmsModel smsModel) {
// 发送短信
this.sendSms(smsModel.getSignName(), smsModel.getTemplateCode(), smsModel.getTemplateParam(),
this.sendSms(smsModel.getAccessKey(), smsModel.getAccessKeySecret(), smsModel.getSignName(), smsModel.getTemplateCode(), smsModel.getTemplateParam(),
smsModel.getTels());
}
/**
*
* @param accessKey accessKey
* @param accessKeySecret accessKeySecret
* @param templateCode SMS_153055065
* @param templateParam {"code":"1111"}
* @param phoneNumbers ,1000
*/
private void sendSms(String signName, String templateCode, Map<String, String> templateParam, List<String> phoneNumbers){
private void sendSms(String accessKey, String accessKeySecret, String signName, String templateCode, Map<String, String> templateParam, List<String> phoneNumbers){
// 验证参数
verify(signName, templateCode, templateParam, phoneNumbers);
verify(accessKey, accessKeySecret, signName, templateCode, templateParam, phoneNumbers);
if(StrUtil.isEmpty(aliyunSmsProperties.getAccessKeyId()) ||
StrUtil.isEmpty(aliyunSmsProperties.getAccessKeySecret())){
// 初始化参数异常
throw new SmsException(SmsMsgCodeEnum.CODE_ERROR_SMS_INIT);
}
SmsClient smsClient = new SmsClient(aliyunSmsProperties.getAccessKeyId(),
aliyunSmsProperties.getAccessKeySecret());
SmsClient smsClient = new SmsClient(accessKey, accessKeySecret);
// 发送信息
SmsTemplate smsTemplate = SmsTemplate.builder()
@ -94,7 +85,24 @@ public class AliYunSmsServiceImpl implements SmsService {
* @param templateParam
* @param phoneNumbers
*/
private void verify(String signName, String templateCode, Map<String, String> templateParam, List<String> phoneNumbers){
private void verify(
String accessKey, String accessKeySecret,
String signName, String templateCode,
Map<String, String> templateParam, List<String> phoneNumbers){
if(StrUtil.isEmpty(accessKey)){
// accessKey不可为空
throw new SmsException(SmsMsgCodeEnum.CODE_ERROR_SMS_ACCESS_KEY_NULL);
}
if(StrUtil.isEmpty(accessKeySecret)){
// accessKeySecret不可为空
throw new SmsException(SmsMsgCodeEnum.CODE_ERROR_SMS_ACCESS_KEY_SECRET_NULL);
}
if(StrUtil.isEmpty(signName)){
// 签名不可为空
throw new SmsException(SmsMsgCodeEnum.CODE_ERROR_SMS_SIG_NAME_NULL);
}
if(StrUtil.isEmpty(signName)){
// 签名不可为空
throw new SmsException(SmsMsgCodeEnum.CODE_ERROR_SMS_SIG_NAME_NULL);

Loading…
Cancel
Save