Commit b1384b46 by WeiCong

调整数据安全框架后台部分

增加数据安全交易配置
parent e32e0dec
......@@ -54,35 +54,9 @@ public abstract class AbstractCommonController {
NoUiContext context = null;
Result ret = null;
String serverEnc = null;
boolean bgidflag=false;
try {
NoUiRequest noUiRequest = new NoUiRequest(request, mappingUrl, dataMap);
//数据安全性拦截处理
if(noUiRequest.isSecurity()){
Map<String, ?> paramsMap = noUiRequest.getParamsMap();
if(paramsMap.containsKey(DataSecurityUtil.CHECK_KEY)){
//加密操作(场景:用户查询指定信息时调用,后续会做修改,删除等操作)
List<String> parlst= (List<String>) paramsMap.get(DataSecurityUtil.CHECK_KEY);
String[] pars= parlst.toArray(new String[0]);
if(ArrayUtils.isEmpty(pars)){
Result rt = new Result(ErrorCodes.ERROR, "调用安全请求方式,但未设置校验数据", null, noUiVersion.getVersion());
return rt;
}
serverEnc=DataSecurityUtil.encrypt(pars,noUiRequest.getUserId());
bgidflag=true;
}else{
//合法性校验操作(场景:用户做修改、删除时调用)
serverEnc= (String) paramsMap.get(DataSecurityUtil.BACKGROUND_ID);
String clientEnc= (String) paramsMap.get(DataSecurityUtil.FRONT_ID);
String errmsg=null;
if((errmsg=DataSecurityUtil.checkIllegalData(serverEnc,clientEnc,noUiRequest.getUserId()))!=null){
Result rt = new Result(ErrorCodes.ERROR, errmsg, null, noUiVersion.getVersion());
return rt;
}
}
}
Alias alias = new Alias(mappingUrl);
String trnName = alias.getTrnName();
Map<String, ?> paramsMap = noUiRequest.getParamsMap();
......@@ -114,6 +88,22 @@ public abstract class AbstractCommonController {
// 模型赋值
NoUiPresentationUtil.hanleInput(context, noUiRequest, alias);
//数据安全性拦截-篡改数据拦截
if(DataSecurityUtil.isSafeMode() && noUiRequest.isSecurity()){
if(paramsMap.containsKey(DataSecurityUtil.BACKGROUND_ID)){
String[] clientpars = DataSecurityUtil.getSafeConfigByTrnName(context,trnName);
if(!ArrayUtils.isEmpty(clientpars)){
//合法性校验操作(场景:用户做修改、删除时调用)
serverEnc= (String) paramsMap.get(DataSecurityUtil.BACKGROUND_ID);
String errmsg=null;
if((errmsg=DataSecurityUtil.checkIllegalData(serverEnc,clientpars,noUiRequest.getUserId()))!=null){
Result rt = new Result(ErrorCodes.ERROR, errmsg, null, noUiVersion.getVersion());
return rt;
}
}
}
}
if (eventType.equals(ON_CLICK)) {
IBaseObject dataField = baseObject(context, noUiRequest, alias);
((IDatafield<?>) dataField).invokeEventRules(context, EventType.ON_CLICK, null);
......@@ -159,9 +149,16 @@ public abstract class AbstractCommonController {
Map<String, Object> afterReturnData = handleReturnData(eventType, context, noUiRequest, alias);
//增加数据安全性代码
if(bgidflag){
afterReturnData.put(DataSecurityUtil.BACKGROUND_ID,serverEnc);
//数据安全性拦截-篡改数据加密
if(DataSecurityUtil.isSafeMode() && noUiRequest.isSecurity()){
if(!paramsMap.containsKey(DataSecurityUtil.BACKGROUND_ID)){
//加密操作(场景:用户查询指定信息时调用,后续会做修改,删除等操作)
String[] pars = DataSecurityUtil.getSafeConfigByTrnName(context,trnName);
if(!ArrayUtils.isEmpty(pars)){
serverEnc=DataSecurityUtil.encrypt(pars,noUiRequest.getUserId());
afterReturnData.put(DataSecurityUtil.BACKGROUND_ID,serverEnc);
}
}
}
ret = ResultUtil.result(NoUiPresentationUtil.retCode(context), NoUiPresentationUtil.retMsg(context), afterReturnData,
......
......@@ -3,11 +3,13 @@ package org.sss.presentation.noui.util;
import log.Log;
import log.LogFactory;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.core.io.ClassPathResource;
import org.sss.common.model.IBaseObject;
import org.sss.common.model.IDatafield;
import org.sss.presentation.noui.common.Constants;
import org.sss.presentation.noui.context.NoUiContext;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.*;
/**
* 前后端数据安全校验,用来防止前端数据被篡改后送到服务端引起的安全问题
......@@ -16,17 +18,74 @@ import java.util.UUID;
public class DataSecurityUtil {
public static final String ENCRYPT_ERROR = "encrypt exception";
public static final String DECRYPT_ERROR = "decrypt exception";
public static final String FIX_STR = "`wD4+-@hh";
public static final String CHECK_KEY="__checkkey__";
public static final String FRONT_ID="__fid__";
public static final String BACKGROUND_ID="__bgid__";
public static final String ERROR_SERVERENC_NULL="[服务端密文串异常],数据存在篡改风险!";
public static final String ERROR_DYNAMICSALT_NULL="[获取的动态盐不存在],可能存在后台暴力破解风险,从而导致数据存在篡改被篡改的风险!";
public static final String ERROR_DYNAMICSALT_EXCEPTION="[获取动态盐异常],可能redis异常,从而导致数据存在篡改被篡改的风险!";
public static final String ERROR_AES_DECODE="[服务端密文串解密失败],数据存在篡改风险!";
public static final String ERROR_NOTMATCH="[服务端密文串解密后的值与客户端密文串不同],数据存在篡改风险!";
public static final String SWITCH_KEY="switch";
public static final String SAFE_MODE="ON";
public static final String UNSAFE_MODE="OFF";
private static HashMap<String, String> securityConfig;
private static final Log log = LogFactory.getLog(DataSecurityUtil.class);
static{
ClassPathResource resource = new ClassPathResource("security.properties",DataSecurityUtil.class);
try {
securityConfig= new HashMap<String, String>((Map) PropertyUtil.load("security.properties"));
} catch (Exception e) {
//降级处理
log.warn("加载安全配置文件失败:"+e.getMessage());
securityConfig=new HashMap<String, String>();
securityConfig.put(SWITCH_KEY,UNSAFE_MODE);
}
}
/**
* 是否开启安全模式,开启后会对前端传输的数据根据配置文件做篡改性校验
* @return true表示开启;false不开启
*/
public static boolean isSafeMode(){
return SAFE_MODE.equalsIgnoreCase(securityConfig.get(SWITCH_KEY));
}
/**
* 获取指定交易的安全配置
* @param context 交易上下文
* @param trnName 交易名
* @return 指定交易的安全配置信息
*/
public static String[] getSafeConfigByTrnName(NoUiContext context, String trnName){
String safeConfigByTrnName = securityConfig.get(trnName);
if(!StringUtil.isEmpty(safeConfigByTrnName)){
String[] urls=safeConfigByTrnName.split(",");
String[] pars= new String[urls.length];
for(int i=0;i<urls.length;i++){
String url=urls[i];
IBaseObject baseObject =context.getSession().getBaseObject(context.getRoot(), url);
if (null == baseObject) {
StringBuilder sb=new StringBuilder();
sb.append("交易[").append(trnName).append("]对应的待校验字段[");
sb.append(url).append("]在模型中未找到");
log.error(sb.toString());
} else {
if (baseObject instanceof IDatafield<?>) {
IDatafield<Object> dataField = (IDatafield<Object>) baseObject;
pars[i]= dataField.getValue().toString();
}else{
StringBuilder sb=new StringBuilder();
sb.append("交易[").append(trnName).append("]对应的待校验字段[");
sb.append(url).append("]在模型中不是IDatafield类型");
log.error(sb.toString());
}
}
}
return pars;
}
return null;
}
/**
* 对不可篡改参数做加签处理
*
......@@ -43,12 +102,7 @@ public class DataSecurityUtil {
log.warn("设置动态盐失败:" + e.getMessage());
return ENCRYPT_ERROR;
}
String content = null;
try {
content = preHandle(pars);
} catch (Exception e) {
log.warn("数据md5加密失败:"+e.getMessage());
}
String content = preHandle(pars);
content = AESUtil.encrypt(content, dynamicSalt.pwd, dynamicSalt.iv);
return content == null ? ENCRYPT_ERROR : content;
}
......@@ -57,11 +111,11 @@ public class DataSecurityUtil {
* 判断数据是否篡改
*
* @param serverEnc 服务端产生的密文串
* @param clientEnc 客户端产生的密文串
* @param clientpars 客户端待校验参数
* @param userId 用户id
* @return 返回错误描述
*/
public static String checkIllegalData(String serverEnc, String clientEnc, String userId) {
public static String checkIllegalData(String serverEnc, String[] clientpars, String userId) {
//1.判断服务端的密文串是否异常
if(StringUtil.isEmpty(serverEnc) || ENCRYPT_ERROR.equals(serverEnc)){
log.warn(ERROR_SERVERENC_NULL);
......@@ -80,14 +134,21 @@ public class DataSecurityUtil {
return ERROR_DYNAMICSALT_EXCEPTION;
}
serverEnc=decrypt(serverEnc,dynamicSalt);
if(StringUtil.isEmpty(serverEnc) || DECRYPT_ERROR.equals(serverEnc)){
log.warn(ERROR_AES_DECODE);
return ERROR_AES_DECODE;
}
String clientEnc=preHandle(clientpars);
if(!serverEnc.equals(clientEnc)){
log.warn(ERROR_NOTMATCH);
return ERROR_NOTMATCH;
}
try {
RedisUtil.delete(getCacheSaltKey(userId));
} catch (Exception e) {
log.warn("删除失效盐出现异常:"+e.getMessage());
}
return null;
}
......@@ -106,13 +167,12 @@ public class DataSecurityUtil {
return content == null ? DECRYPT_ERROR : content;
}
private static String preHandle(String[] pars) throws Exception {
private static String preHandle(String[] pars){
if (ArrayUtils.isEmpty(pars)) {
return null;
}
List<String> lst = Arrays.asList(pars);
String md5=String.join(FIX_STR, lst);
md5=StringUtil.encryptMD5(md5);
String md5=String.join("", lst);
return md5;
}
......@@ -121,7 +181,6 @@ public class DataSecurityUtil {
Object obj = RedisUtil.get(key);
DynamicSalt rs=parseDynamicSalt(obj);
if(rs != null){
RedisUtil.delete(key);
return rs;
}
return null;
......
#安全开关(ON:开;OFF:关)
switch=ON
#客户管理——修改
dbepty=\\ptygrp\\rec\\inr,\\ptygrp\\rec\\extkey
#客户管理——删除
dbdpty=\\ptygrp\\rec\\inr
#
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