Commit e32e0dec by WeiCong

1.通过后端AES数据加密(动态盐)+二次校验,解决数据篡改问题

2.解决用户注销、关闭浏览器后依旧可以访问pdf的缺陷
parent e58d0f03
package org.sss.presentation.noui.api.request;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.sss.presentation.noui.common.Constants;
import org.sss.presentation.noui.context.NoUiContextManager;
import org.sss.presentation.noui.util.StringUtil;
public class NoUiRequest {
private HttpServletRequest httpRequest;
private String token;
private String userId;
private String terminalType;// WEB APP
private String mappingUrl;
private boolean openSource;
private Map<String, ?> paramsMap = new HashMap<String, Object>();
private Map<String, ?> dataMap = new HashMap<String, Object>();
private Map<String, ?> saveDisplayMap = new HashMap<String, Object>();
public NoUiRequest() {
}
public NoUiRequest(HttpServletRequest request, String mappingUrl, Map<String, ?> requestData) {
String tokenId = request.getHeader("token");
String userId = request.getHeader("userId");
String terminalType = request.getHeader("terminalType");
this.token = tokenId;
this.userId = userId;
this.terminalType = terminalType;
this.mappingUrl = mappingUrl;
if(request.getRequestURI().indexOf(NoUiContextManager.openSourcePrefix+"/") >= 0)
{
this.openSource = true; //开放访问路径
}
if (requestData != null) {
this.paramsMap = (Map<String, ?>) requestData.get(Constants.PARAMS);
this.dataMap = (Map<String, ?>) requestData.get(Constants.DATA);
this.saveDisplayMap = (Map<String, ?>) requestData.get(Constants.DISPLAY);
if (this.paramsMap == null)
this.paramsMap = new HashMap<String, Object>();
if (this.dataMap == null)
this.dataMap = new HashMap<String, Object>();
if (this.saveDisplayMap == null)
this.saveDisplayMap = new HashMap<String, Object>();
}
}
public boolean isOpenSource(){
return this.openSource;
}
public String getUserId() {
return userId;
}
public String getMappingUrl() {
return mappingUrl;
}
public void setUserId(String userId) {
this.userId = userId;
}
public void setMappingUrl(String mappingUrl) {
this.mappingUrl = mappingUrl;
}
public HttpServletRequest getHttpRequest() {
return httpRequest;
}
public void setHttpRequest(HttpServletRequest httpRequest) {
this.httpRequest = httpRequest;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getTerminalType() {
return terminalType;
}
public void setTerminalType(String terminalType) {
this.terminalType = terminalType;
}
public Map<String, ?> getParamsMap() {
return paramsMap;
}
public Map<String, ?> getDataMap() {
return dataMap;
}
public void setParamsMap(Map<String, ?> paramsMap) {
this.paramsMap = paramsMap;
}
public void setDataMap(Map<String, ?> dataMap) {
this.dataMap = dataMap;
}
public Map<String, ?> getSaveDisplayMap() {
return saveDisplayMap;
}
public void setSaveDisplayMap(Map<String, ?> saveDisplayMap) {
this.saveDisplayMap = saveDisplayMap;
}
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class NoUiRequest {
private HttpServletRequest httpRequest;
private String token;
private String userId;
private String terminalType;// WEB APP
private String mappingUrl;
private boolean openSource;
private Map<String, ?> paramsMap = new HashMap<String, Object>();
private Map<String, ?> dataMap = new HashMap<String, Object>();
private Map<String, ?> saveDisplayMap = new HashMap<String, Object>();
private boolean isSecurity=false;
public NoUiRequest() {
}
public NoUiRequest(HttpServletRequest request, String mappingUrl, Map<String, ?> requestData) {
String tokenId = request.getHeader("token");
String userId = request.getHeader("userId");
String terminalType = request.getHeader("terminalType");
String security=request.getHeader("security");
this.token = tokenId;
this.userId = userId;
this.terminalType = terminalType;
this.mappingUrl = mappingUrl;
if(!StringUtil.isEmpty(security)){
this.isSecurity=true;
}
if (request.getRequestURI().indexOf(NoUiContextManager.openSourcePrefix + "/") >= 0) {
this.openSource = true; //开放访问路径
}
if (requestData != null) {
this.paramsMap = (Map<String, ?>) requestData.get(Constants.PARAMS);
this.dataMap = (Map<String, ?>) requestData.get(Constants.DATA);
this.saveDisplayMap = (Map<String, ?>) requestData.get(Constants.DISPLAY);
if (this.paramsMap == null)
this.paramsMap = new HashMap<String, Object>();
if (this.dataMap == null)
this.dataMap = new HashMap<String, Object>();
if (this.saveDisplayMap == null)
this.saveDisplayMap = new HashMap<String, Object>();
}
}
public boolean isOpenSource() {
return this.openSource;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getMappingUrl() {
return mappingUrl;
}
public void setMappingUrl(String mappingUrl) {
this.mappingUrl = mappingUrl;
}
public HttpServletRequest getHttpRequest() {
return httpRequest;
}
public void setHttpRequest(HttpServletRequest httpRequest) {
this.httpRequest = httpRequest;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getTerminalType() {
return terminalType;
}
public void setTerminalType(String terminalType) {
this.terminalType = terminalType;
}
public Map<String, ?> getParamsMap() {
return paramsMap;
}
public void setParamsMap(Map<String, ?> paramsMap) {
this.paramsMap = paramsMap;
}
public Map<String, ?> getDataMap() {
return dataMap;
}
public void setDataMap(Map<String, ?> dataMap) {
this.dataMap = dataMap;
}
public Map<String, ?> getSaveDisplayMap() {
return saveDisplayMap;
}
public void setSaveDisplayMap(Map<String, ?> saveDisplayMap) {
this.saveDisplayMap = saveDisplayMap;
}
public boolean isSecurity() {
return isSecurity;
}
}
......@@ -3,6 +3,7 @@ package org.sss.presentation.noui.controller;
import log.Log;
import log.LogFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
......@@ -19,7 +20,7 @@ import org.sss.presentation.noui.context.NoUiContext;
import org.sss.presentation.noui.context.NoUiContextManager;
import org.sss.presentation.noui.context.NoUiPresentation;
import org.sss.presentation.noui.jwt.RedisLoginInfo;
import org.sss.presentation.noui.util.BizKeySetManager;
import org.sss.presentation.noui.util.DataSecurityUtil;
import org.sss.presentation.noui.util.NoUiPresentationUtil;
import org.sss.presentation.noui.util.RedisUtil;
import org.sss.presentation.noui.util.StringUtil;
......@@ -46,28 +47,48 @@ public abstract class AbstractCommonController {
public String getMainPanel(){
return "";
}
@SuppressWarnings("unchecked")
public Object event(String mappingUrl, String eventType, Map<String, Object> dataMap, MultipartFile file,HttpServletRequest request, HttpServletResponse response) {
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();
if(!BizKeySetManager.validateParasMap(eventType,trnName,paramsMap))
{
return ResultUtil.result(ErrorCodes.ILLEGAL_ARGS, "不合法的参数", "", noUiVersion.getVersion());
}
context = NoUiContextManager.createNoUiContext(noUiRequest);
// 交易参数赋值
for (String key : paramsMap.keySet()) {
context.getSession().storeData(key, paramsMap.get(key));
}
......@@ -80,10 +101,10 @@ public abstract class AbstractCommonController {
NoUiPresentationUtil.setSysmod(context, (byte[]) redisLoginInfo.getSysmod());
context.setRedisLoginInfo(redisLoginInfo);
}
// 交易跳转
context.getSession().chain(true, trnName);
//执行可能存在的主面板的初始化
if(this.getMainPanel().length() > 0)
{
......@@ -135,10 +156,14 @@ public abstract class AbstractCommonController {
redisLoginInfo.setSysmod(sysmodBytes);
RedisUtil.set(StringUtil.userUniqueId(noUiRequest), redisLoginInfo);
}
Map<String, Object> afterReturnData = handleReturnData(eventType, context, noUiRequest, alias);
//增加数据安全性代码
if(bgidflag){
afterReturnData.put(DataSecurityUtil.BACKGROUND_ID,serverEnc);
}
ret = ResultUtil.result(NoUiPresentationUtil.retCode(context), NoUiPresentationUtil.retMsg(context), afterReturnData,
NoUiPresentationUtil.handleErrorReturnData(context, alias), NoUiPresentationUtil.handleCodeTableReturnData(context, alias), noUiVersion.getVersion());
} catch (Exception e) {
......@@ -175,7 +200,7 @@ public abstract class AbstractCommonController {
}
String realPath = alias.getRelPath(aliasKey);
IBaseObject baseObject = context.getSession().getBaseObject(context.getRoot(), realPath);
data.put(aliasKey, NoUiPresentationUtil.handIBaseObject(context, baseObject, realPath));
}
return data;
......@@ -221,7 +246,7 @@ public abstract class AbstractCommonController {
data.put(key, NoUiPresentationUtil.handIBaseObject(context, modifyMap.get(key), key));
}
}
return data;
}
}
......@@ -97,6 +97,7 @@ public class LoginController {
NoUiUtils.logout(userId,"*"); //清理可能存在的历史缓存
RedisUtil.set(StringUtil.userUniqueId(noUiRequest), redisLoginInfo);
RedisUtil.set(StringUtil.getCacheSessionId(noUiRequest.getUserId()),request.getSession().getId());
//解决初次登陆,超期限登陆
final Object o = map.get(ERROR);
......
......@@ -41,7 +41,7 @@ public class ResourceAccessFilter implements Filter {
if (!doPdfsFilter(uri, pdfres, request, response)) {
return;
}
} else if (isExcludeRes(uri) || request.getSession().getAttribute("token") == null) {
} else if (isExcludeRes(uri)) {
response.setStatus(403);
forbidden(request, response);
} else {
......@@ -53,11 +53,19 @@ public class ResourceAccessFilter implements Filter {
}
}
private boolean doPdfsFilter(String uri, String pdfres, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (request.getSession().getAttribute("token") == null) {
log.warn("Access Pdfs Forbidden");
return forbiddenPdf(request, response);
private boolean isNotSameSessionId(String userId, HttpServletRequest request) throws Exception {
String realSessionId = (String) RedisUtil.get(StringUtil.getCacheSessionId(userId));
String sessionId = request.getSession().getId();
if (StringUtil.isEmpty(realSessionId)) {
return true;
}
if (!realSessionId.equals(sessionId)) {
return true;
}
return false;
}
private boolean doPdfsFilter(String uri, String pdfres, HttpServletRequest request, HttpServletResponse response) throws Exception {
String[] parts = uri.split("_");
if (parts.length != 3) {
log.warn("Access Pdfs Forbidden");
......@@ -71,7 +79,7 @@ public class ResourceAccessFilter implements Filter {
return forbiddenPdf(request, response);
} else {
//校验usrid+token+固定值的加密
if (!isLegalSec(sec, uid, res)) {
if (!isLegalSec(sec, uid, res, request)) {
log.warn("Access Pdfs Forbidden");
return forbiddenPdf(request, response);
}
......@@ -81,11 +89,14 @@ public class ResourceAccessFilter implements Filter {
return false;
}
private boolean isLegalSec(String sec, String uid, String res) throws Exception {
private boolean isLegalSec(String sec, String uid, String res, HttpServletRequest request) throws Exception {
if (res.lastIndexOf("/") > 0) {
res = res.substring(res.lastIndexOf("/") + 1);
}
String rawuid = new StringBuilder(uid).reverse().toString();
if (isNotSameSessionId(rawuid,request)) {
return false;
}
Object obj = RedisUtil.get(KEY.replace("##", rawuid));
if (obj == null) {
return false;
......
......@@ -17,54 +17,12 @@ import org.sss.presentation.noui.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class TokenInterceptor implements HandlerInterceptor {
private static Map<String, Integer> CounterMap = new ConcurrentHashMap<>(); //计数map
@Autowired
private NoUiVersion noUiVersion;
public static int compareAndSet(String token, int cnt, int max_curr_cnt) {
synchronized (CounterMap) {
int count = getCount(token);
if (count >= max_curr_cnt)
return -1;
count += cnt;
if (count <= 0)
CounterMap.remove(token);
else
CounterMap.put(token, count);
return count;
}
}
public static int addCount(String token, int cnt) {
synchronized (CounterMap) {
int count = getCount(token);
count += cnt;
if (count <= 0)
CounterMap.remove(token);
else
CounterMap.put(token, count);
return count;
}
}
public static int getCount(String token) {
Integer count = CounterMap.get(token);
if (count == null)
return 0;
return count;
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception arg3) throws Exception {
String token = request.getHeader("token");
if (!StringUtil.isEmpty(token) && !token.startsWith(Constants.BACKGROUND_FLAG)){
addCount(token, -1); //计算器减1
}
NoUiUtils.clearLoginInfo();
}
......@@ -124,12 +82,6 @@ public class TokenInterceptor implements HandlerInterceptor {
responseMessage(response, response.getWriter(), rt);
return false;
}
//超过最大并发数限制
if (compareAndSet(noUiRequest.getToken(), 1, noUiVersion.getCurr_max_num()) < 0) {
Result rt = new Result(ErrorCodes.GT_MAX_CURR_NUM, "超过单个会话并发数", null, noUiVersion.getVersion());
responseMessage(response, response.getWriter(), rt);
return false;
}
// 重新刷入登陆时间
RedisLoginInfo nweRedisLoginInfo = new RedisLoginInfo(userId, token, NumericUtil.sessionTimeOut(), redisLoginInfo.getSysmod(), noUiRequest.getTerminalType());
RedisUtil.set(Constants.SESSION + "." + userId + "." + terminalType, nweRedisLoginInfo);
......
package org.sss.presentation.noui.util;
import com.auth0.jwt.internal.org.apache.commons.codec.binary.Hex;
import log.Log;
import log.LogFactory;
import sun.misc.BASE64Decoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
public class AESUtil {
private static final Log log = LogFactory.getLog(NoUiUtils.class);
private final static String password = "1qaz@Wsx#eDC";//目前使用
private final static String IV = "#EdcxSW@1qaz3rfv";//目前使用
private final static String patten = "^[0-9]+$";
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";//默认的加密算法
public static String decryptAES(String content,String code) throws Exception {
public static String decryptAES(String content, String code) throws Exception {
//int len=content.length()-1;
SecretKeySpec skeySpec = new SecretKeySpec(getKey(code).getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec skeySpec = new SecretKeySpec(getKey(code).getBytes(StandardCharsets.UTF_8), KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
/*if(content.substring(len,len+1).matches("^[0-9]+$")){
......@@ -27,7 +35,7 @@ public class AESUtil {
}
}*/
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(content);// 先用bAES64解密
return new String(cipher.doFinal(encrypted1));
return new String(cipher.doFinal(encrypted1), StandardCharsets.UTF_8);
}
public static String getKey(String code) {
......@@ -43,9 +51,96 @@ public class AESUtil {
return key;
}
/**
* @param content 待加签串
* @param password 动态密码
* @param iv 动态向量iv
* @return aes加密后的16进制字符串
*/
public static String encrypt(String content, String password, String iv) {
if (unsatisfied(content, password, iv)) {
return null;
}
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(StandardCharsets.UTF_8), KEY_ALGORITHM);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);
byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
byte[] result = cipher.doFinal(byteContent);
return Hex.encodeHexString(result);
// return Base64.getEncoder().encodeToString(result);
} catch (Exception ex) {
log.warn("对数据进行aes加密异常:" + ex.getMessage(), ex);
}
return null;
}
/**
* @param content 待解签的16进制字符串
* @param password 动态密码
* @param iv 动态向量iv
* @return aes解密后的原始串
*/
public static String decrypt(String content, String password, String iv) {
if (unsatisfied(content, password, iv)) {
return null;
}
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(StandardCharsets.UTF_8), KEY_ALGORITHM);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
// byte[] result = cipher.doFinal(Base64.getDecoder().decode(content));
byte[] result = cipher.doFinal(Hex.decodeHex(content.toCharArray()));
return new String(result, StandardCharsets.UTF_8);
} catch (Exception ex) {
log.warn("对数据进行aes加密异常:" + ex.getMessage(), ex);
}
return null;
}
private static boolean unsatisfied(String content, String password, String iv) {
if (StringUtil.isEmpty(content)) {
return true;
}
if (StringUtil.isEmpty(password) || password.length() != 16) {
return true;
}
if (StringUtil.isEmpty(iv) || iv.length() != 16) {
return true;
}
return false;
}
public static void main(String[] args) {
try {
System.out.println(decryptAES("L2eRe4wOLeyqvUIayLs1NA==","7d9t"));
//测登陆密码解密
// System.out.println(decryptAES("L2eRe4wOLeyqvUIayLs1NA==","7d9t"));
//测数据安全性加解密和相关性能
/*long beg=System.currentTimeMillis();
for(int i=0;i<1000;i++){
String enc=encrypt("hello 中国,13232",password+"0000",IV);
System.out.println("encrypt:"+enc);
String dec=decrypt(enc,password+"0000",IV);
System.out.println("decrypt:"+dec);
}
long end=System.currentTimeMillis();
System.out.println(end-beg);*/
//数据安全加解密逻辑验证
String[] str=new String[]{"hello中国","13232"};
List<String> lst = Arrays.asList(str);
String md5=String.join("`wD4+-@hh", lst);
System.out.println("md5前=="+md5);
md5=StringUtil.encryptMD5(md5);
System.out.println("md5后=="+md5);
String enc=encrypt(md5,password+"0000",IV);
System.out.println("encrypt:"+enc);
String dec=decrypt(enc,password+"0000",IV);
System.out.println("decrypt:"+dec);
} catch (Exception e) {
e.printStackTrace();
}
......
package org.sss.presentation.noui.util;
import log.Log;
import log.LogFactory;
import org.apache.commons.lang.ArrayUtils;
import org.sss.presentation.noui.common.Constants;
import java.util.Arrays;
import java.util.List;
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="[服务端密文串解密后的值与客户端密文串不同],数据存在篡改风险!";
private static final Log log = LogFactory.getLog(DataSecurityUtil.class);
/**
* 对不可篡改参数做加签处理
*
* @param pars 待加签参数
* @param userId 用户ID
* @return 参数加签串
*/
public static String encrypt(String[] pars, String userId) {
DynamicSalt dynamicSalt = new DynamicSalt();
dynamicSalt.init();
try {
setDynamicSaltFromRedis(dynamicSalt, userId);
} catch (Exception e) {
log.warn("设置动态盐失败:" + e.getMessage());
return ENCRYPT_ERROR;
}
String content = null;
try {
content = preHandle(pars);
} catch (Exception e) {
log.warn("数据md5加密失败:"+e.getMessage());
}
content = AESUtil.encrypt(content, dynamicSalt.pwd, dynamicSalt.iv);
return content == null ? ENCRYPT_ERROR : content;
}
/**
* 判断数据是否篡改
*
* @param serverEnc 服务端产生的密文串
* @param clientEnc 客户端产生的密文串
* @param userId 用户id
* @return 返回错误描述
*/
public static String checkIllegalData(String serverEnc, String clientEnc, String userId) {
//1.判断服务端的密文串是否异常
if(StringUtil.isEmpty(serverEnc) || ENCRYPT_ERROR.equals(serverEnc)){
log.warn(ERROR_SERVERENC_NULL);
return ERROR_SERVERENC_NULL;
}
//2.用aes动态盐解密服务端密文串与客户端的密文串比对
DynamicSalt dynamicSalt;
try {
dynamicSalt=getDynamicSaltFromRedis(userId);
if(dynamicSalt==null){
log.warn(ERROR_DYNAMICSALT_NULL);
return ERROR_DYNAMICSALT_NULL;
}
} catch (Exception e) {
log.warn(ERROR_DYNAMICSALT_EXCEPTION+e.getMessage());
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;
}
if(!serverEnc.equals(clientEnc)){
log.warn(ERROR_NOTMATCH);
return ERROR_NOTMATCH;
}
return null;
}
/**
* 获取指定用户缓存盐的key
* @param userId 用户ID
* @return 返回指定用户缓存盐的key
*/
public static String getCacheSaltKey(String userId) {
String setKey = new StringBuilder(Constants.SESSION).append(".").append(userId).append(".CACHE_SALT").toString();
return setKey;
}
private static String decrypt(String content, DynamicSalt dynamicSalt) {
content = AESUtil.decrypt(content, dynamicSalt.pwd, dynamicSalt.iv);
return content == null ? DECRYPT_ERROR : content;
}
private static String preHandle(String[] pars) throws Exception {
if (ArrayUtils.isEmpty(pars)) {
return null;
}
List<String> lst = Arrays.asList(pars);
String md5=String.join(FIX_STR, lst);
md5=StringUtil.encryptMD5(md5);
return md5;
}
private static DynamicSalt getDynamicSaltFromRedis(String userId) throws Exception {
String key=getCacheSaltKey(userId);
Object obj = RedisUtil.get(key);
DynamicSalt rs=parseDynamicSalt(obj);
if(rs != null){
RedisUtil.delete(key);
return rs;
}
return null;
}
private static DynamicSalt parseDynamicSalt(Object raw){
if(raw instanceof String){
String[] parts= ((String) raw).split("_");
if(parts != null && parts.length ==2){
return new DynamicSalt(parts[0],parts[1]);
}
}else if(raw instanceof DynamicSalt){
return (DynamicSalt) raw;
}
return null;
}
private static void setDynamicSaltFromRedis(DynamicSalt dynamicSalt, String userId) throws Exception {
RedisUtil.set(getCacheSaltKey(userId), dynamicSalt.toString());
}
static class DynamicSalt {
private String iv;
private String pwd;
public DynamicSalt() {
}
public DynamicSalt(String iv, String pwd) {
this.iv = iv;
this.pwd = pwd;
}
public void init() {
String uuid = UUID.randomUUID().toString().replace("-", "");
iv = uuid.substring(0, 16);
pwd = uuid.substring(16);
}
@Override
public String toString() {
return iv+"_"+pwd;
}
}
}
......@@ -7,45 +7,56 @@ import java.security.MessageDigest;
public class StringUtil {
public static boolean isEmpty(String str) {
if (str == null || str.trim().equals(""))
return true;
return false;
}
public static String userUniqueId(NoUiRequest request) {
return Constants.SESSION + "." + request.getUserId() + "." + request.getTerminalType();
}
/**
* MD5加密字符串
* @param inStr
* @return
* @throws Exception
*/
public static String encryptMD5(String inStr) throws Exception
{
MessageDigest md5 = null;
md5 = MessageDigest.getInstance("MD5");
char[] charArray = inStr.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++)
{
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16)
hexValue.append("0");
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
public static boolean isEmpty(String str) {
if (str == null || str.trim().equals("")){
return true;
}
return false;
}
public static String userUniqueId(NoUiRequest request) {
return Constants.SESSION + "." + request.getUserId() + "." + request.getTerminalType();
}
/**
* 获取指定用户缓存的sessionid
*
* @param userId 用户ID
* @return 返回指定用户登陆客户端的sessionid
*/
public static String getCacheSessionId(String userId) {
String setKey = new StringBuilder(Constants.SESSION).append(".").append(userId).append(".CACHE_SESSION").toString();
return setKey;
}
/**
* MD5加密字符串
*
* @param inStr
* @return
* @throws Exception
*/
public static String encryptMD5(String inStr) throws Exception {
MessageDigest md5 = null;
md5 = MessageDigest.getInstance("MD5");
char[] charArray = inStr.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16)
hexValue.append("0");
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
}
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