校验数据完整性

所有的模块的输入都需要进行数据完整性校验,需要增加如下的额外字段,

名称

字段

类型

是否必须

数据校验码

sign

String

必须

所有模块的接口使用Json格式,该校验码的计算方式如下:

将所有输入字段按照ASCII码表进行排序(无需sign字段),然后格式为key=value(例如userId=123456),然后将数值使用字符“&”(半角的&字符)连接,并进行SM3运算,校验码为SM3的运算结果。

为了让包含中文的URL可以使用,需要在SM3计算之前,进行UrlEncode编码

例如:用户登录接口,加密步骤如下

platformId=1&authPassword=417938&challageCode=&userId=1

  1. 按照字段ASCII排序,排序结果为

authPassword=417938&challageCode=&platformId=1&userId=1

  1. 添加加密KEY: PEOPLESEC2020  

在排序完成后在字符串后面拼接上加密KEY:PEOPLESEC2020

authPassword=417938&challageCode=&platformId=1&userId=1PEOPLESEC2020

  1. 将上面的字符串进行UrlEncode编码

(例子中的“=”被替换为了“%3D”,“&”被替换为了“%26”)编码后的结果为:

authPassword%3D417938%26challageCode%3D%26platformId%3D1%26userId%3D1PEOPLESEC2020

  1. 将上面的字符串进行SM3加密

加密结果为

09a1c02abdf79102b145cd18d855248c8828069ce1a388d0143a2fd3d593bac5

package com.people.util;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import com.google.gson.Gson;
import com.people.domain.QueryCriminalRecordResponseVo;
import com.people.domain.SceneNameVo;
import com.people.domain.SourceDataVo;
import com.people.security.encrypt.Sha256;
import com.people.security.sm3.SM3Utils;

/**
 * @author qi_zhou
 * @description 校验参数完整性工具类
 * @date: 2019-09-20
 */
public class SignUtil {
//	private static final String ENCRYPT_KEY = "PEOPLESEC2020";

	public static String createSign(String characterEncoding,
			SortedMap parameters) {
		StringBuffer sb = new StringBuffer();
		for (Map.Entry entry : parameters.entrySet()) {
			if (!StringUtils.isEmpty(entry.getValue())
					&& !"sign".equals(entry.getKey())
					&& !"key".equals(entry.getKey())) {
				sb.append(entry.getKey() + "=" + entry.getValue() + "&");
			}
		}
		String s = sb.toString();
		if (s.length() > 0) {
			s = s.substring(0, sb.toString().length() - 1);
		}
		String sign = MakeHEX.md5(s).toUpperCase();
		return sign;
	}

	/**
	 * 根据对象,检查对象SING值是否存在且正确
	 *
	 * @param obj
	 *            需要计算SIGN值的任意对象
	 * @return 对象SIGN值是否存在且正确
	 */
	public static boolean checkSign(Object obj,String interfaceKey) {
		String[] signRight = calcSign(obj,interfaceKey);
		try {
			Field field = FieldUtils.getField(obj.getClass(), "sign", true);
			if (field == null) {
				return false;
			}
			String sign = (String) FieldUtils.readField(obj, "sign", true);
			sign = sign.replace(" ", "");
			return StringUtils.equalsIgnoreCase(sign, signRight[0])
					|| StringUtils.equalsIgnoreCase(sign, signRight[1]);
		} catch (IllegalAccessException e) {
			Slf4jLogUtil.error(e.getMessage(), e);
			return false;
		}
	}

	/**
	 * 根据对象,计算SIGN值
	 *
	 * @param map
	 *            需要计算SIGN值的任意对象
	 * @return SIGN值
	 */
	public static String[] calcSign(Map map,String ENCRYPT_KEY) {
		try {
			String paramsStr = toQueryString(map);
			// Slf4jLogUtil.info("Request paramsStr : " + paramsStr);
			String tempStrFirst = URLEncoder.encode(paramsStr, "UTF-8") + ""
					+ ENCRYPT_KEY;
			Slf4jLogUtil.debug(tempStrFirst);
			
			// String resultFirst = MakeHEX.md5(tempStrFirst).toLowerCase();
//			 String resultFirst = Sha256.getSHA256(tempStrFirst);
			String resultFirst = SM3Utils.SM3Encrypt(tempStrFirst);
			// 修正BUG:空格(“ ”)转换成了(+)
			String tempStrSecond = tempStrFirst.replace("+", "%20").replace(
					"*", "%2A");
			// String resultSecond = MakeHEX.md5(tempStrSecond).toLowerCase();
			String resultSecond = SM3Utils.SM3Encrypt(tempStrSecond);
			return new String[] { resultFirst, resultSecond };
		} catch (Exception e) {
			Slf4jLogUtil.error(e.getMessage(), e);
			return new String[] { "", "" };
		}
	}

	/**
	 * 根据对象,计算SIGN值
	 *
	 * @param obj
	 *            需要计算SIGN值的任意对象
	 * @return sign值
	 */
	public static String[] calcSign(Object obj,String interfaceKey) {
		Map paramsMap = convertToMap(obj);
		return calcSign(paramsMap, interfaceKey);
	}

	/**
	 * 将obj里面的所有字段获取并按照ASCLL码顺序排序
	 *
	 * @param obj
	 *            等待处理的对象
	 * @return Map对象
	 */
	private static Map convertToMap(Object obj) {
		Map map = new TreeMap<>();
		List fields = FieldUtils.getAllFieldsList(obj.getClass());
		for (Field field : fields) {
			String fieldName = field.getName();
			Object fieldValue = "";
			try {
				fieldValue = FieldUtils.readField(field, obj, true);
			} catch (IllegalAccessException e) {
				Slf4jLogUtil.error(e.getMessage(), e);
			}
			if (fieldValue != null && !StringUtils.equals(fieldName, "sign")) {
				map.put(fieldName, fieldValue);
			}
		}
		return map;
	}

	/**
	 * 对Map内所有value作utf8编码,拼接返回结果
	 *
	 * @param data
	 *            等待处理的Map对象
	 * @return 字符串拼接的返回结果
	 * @throws UnsupportedEncodingException
	 *             不支持的编码异常
	 */
	private static String toQueryString(Map data)
			throws UnsupportedEncodingException {
		StringBuilder queryString = new StringBuilder();
		for (Map.Entry pair : data.entrySet()) {
			queryString.append(pair.getKey()).append("=");
			// queryString.append(URLEncoder.encode(String.valueOf(pair.getValue()),
			// "UTF-8") + "&");
			queryString.append(String.valueOf(pair.getValue())).append("&");
		}
		if (queryString.length() > 0) {
			queryString.deleteCharAt(queryString.length() - 1);
		}
		return queryString.toString();
	}
	/**
	  * 初始化请求参数
	  * 
	  * @param vo
	  * @return
	  */
	 public static SortedMap initQueryInfoMap(SceneNameVo vo) {
	  Map map = new HashMap();
	  map.put("custNumber", vo.getCustNumber());
	  map.put("sceneName", vo.getSceneName());
	  return new TreeMap<>(map);
	 }
	 /**
	  * 初始化请求参数
	  * 
	  * @param vo
	  * @return
	  */
	 public static SortedMap initQueryInfoMap(SourceDataVo vo) {
	  Map map = new HashMap();
	  map.put("custNumber", vo.getCustNumber());
	  map.put("requestName", vo.getRequestName());
	  return new TreeMap<>(map);
	 }
	public static void main(String arg[]) throws Exception {
		
		Gson gson = new Gson();
		List> list=new ArrayList>();
		
		SortedMap map = new TreeMap();
//		map.put("queryId", "1");
		Map params = new HashMap();
		params.put("idCard", "640651354422655445");
		params.put("userName", "zzq");
		list.add(params);
		Map params2 = new HashMap();
		params2.put("idCard", "6406513544226554452");
		params2.put("userName", "zzq2");
		list.add(params2);
		String json = gson.toJson(list);
		String encrypt = AesUtils.encrypt(json, AesUtils.KEY);
		map.put("cipherInfo", json);
		// 将集合通过aes加密
//		String[] signRight = SignUtil.calcSign(map, "PEOPLESEC2020");
//		String sign = signRight[0];
//		Map params22 = new HashMap();
//		params22.put("queryId", "1");
//		params22.put("sign", sign);
//		String json2 = gson.toJson(params22);
//		System.out.println(json2);
	
	

	}
	 
	  
}

列子:

客户端请求

// 获取客户编号
			InterfaceInfoVo selectInfoKey = interfaceInfoDao.selectInfoKey();
			// 调用服务端接口
			Map params = new HashMap();
			params.put("cipherInfo", Long.parseLong(appId));
			params.put("queryName", selectInfoKey.getCustNumber());
			SortedMap map = new TreeMap();
			map.put("cipherInfo", appId);
			map.put("queryName", selectInfoKey.getCustNumber());
			String[] signRight = SignUtil.calcSign(map,
					selectInfoKey.getInterfaceKey());
			String msign = signRight[0];
			params.put("sign", msign);
			String json = gson.toJson(params);
			String postParameters = OkHttpUtil.postJsonParams(url + downloadApp, json);

服务端校验

	private boolean verifySign(QueryCriminalRecordRequestVo vo,
			String interfaceKey) throws Exception {
		String[] signRight = SignUtils.calcSign(
				initQueryMap(vo.getCipherInfo(), vo.getQueryName()),
				interfaceKey);
		// 验证签名参数
		if (!(StringUtils.equalsIgnoreCase(vo.getSign(), signRight[0]) || StringUtils
				.equalsIgnoreCase(vo.getSign(), signRight[1]))) {
			return Boolean.FALSE;
		}
		return Boolean.TRUE;
	}

	private Map initQueryMap(String cipherInfo, String queryName) {
		Map map = new HashMap();
		map.put("cipherInfo", cipherInfo);
		map.put("queryName", queryName);
		return new TreeMap<>(map);
	}
package com.people.domain;

import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;

import javax.validation.constraints.NotEmpty;

public class QueryCriminalRecordRequestVo implements Serializable{
	@ApiModelProperty(value = "sign")
	@NotEmpty(message = "签名信息不能为空")
	private String sign;//签名
	@ApiModelProperty(value = "cipherInfo")
	@NotEmpty(message = "数据集合不能为空")
	private String cipherInfo;//密文信息
	@NotEmpty(message = "查询名称不能为空")
	private String queryName;//密文信息
	public String getSign() {
		return sign;
	}
	public void setSign(String sign) {
		this.sign = sign;
	}
	public String getCipherInfo() {
		return cipherInfo;
	}
	public void setCipherInfo(String cipherInfo) {
		this.cipherInfo = cipherInfo;
	}
	public String getQueryName() {
		return queryName;
	}
	public void setQueryName(String queryName) {
		this.queryName = queryName;
	}
	
	

}

服务端校验第二种方法

private Object checkmakeloadKey(String code, String sign) {
		ChckCodeSign check = new ChckCodeSign();
		check.setCode(code);
		check.setSign(sign);
		BaseVo baseVo = check;
		if (!SignUtil.checkSign(baseVo)) {
			return ResponseUtils.map("-101",
					i18nUtils.getKey("result.err.sign"));
		}
		return null;

	}
package com.people.domain.finsherman.sign;

import java.io.Serializable;

import com.people.domain.BaseVo;

public class ChckCodeSign extends BaseVo implements Serializable {

	private String code;
	private String sign;
	public String getCode() {
		return code;
	}
	public void setCode(String code) {
		this.code = code;
	}
	public String getSign() {
		return sign;
	}
	public void setSign(String sign) {
		this.sign = sign;
	}

	
}
package com.people.domain;

import java.io.Serializable;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;

/**
 * 基类VO
 */
@SuppressWarnings("serial")
public class BaseVo implements Serializable{
	@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
	private String sign; //签名验证

	public String getSign() {
		return sign;
	}

	public void setSign(String sign) {
		this.sign = sign;
	}

}

服务端校验第三种方法(切面校验)

package com.people.aspect;

import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.google.gson.Gson;
import com.people.common.utils.I18nUtils;
import com.people.common.utils.ResponseUtils;
import com.people.common.utils.Slf4jLogUtil;
import com.people.domain.BaseVo;
import com.people.util.SignUtil;

// 定义一个切面
@Configuration
@Aspect
public class CheckSignAspect {
	@Autowired
	private I18nUtils i18nUtils;
	Gson gson = new Gson();

	/**
	 * Controller 接口调用方法切入点
	 */
	@Pointcut("@annotation(com.people.annotation.CheckSign)")
	public void jsonMethodCall() {
		// 定义一个pointcut
	}

	/**
	 * 调用外部接口时,检查sn和snTime字是否合法
* 若不合法,停止接口调用并且返回错误消息 */ @Around(value = "jsonMethodCall()") public Object checkControllerObjectSn(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); BaseVo baseVo = toBaseVo(args); if (null == baseVo || StringUtils.isEmpty(baseVo.getSign())) { return ResponseUtils.map("-101", i18nUtils.getKey("result.err.sign")); } printRequestInput(baseVo); Object result = null; if (!SignUtil.checkSign(baseVo)) { result = ResponseUtils.map("-101", i18nUtils.getKey("result.err.sign")); } else { result = pjp.proceed(); } printRequestOutput(baseVo, result); return result; } private BaseVo toBaseVo(Object[] args) { if (args != null && args.length >= 1 && args[0] != null && args[0] instanceof BaseVo) { return (BaseVo) args[0]; } return null; } private String toRequestSn(BaseVo baseVo) { if (baseVo == null) { return "null"; } return baseVo.getSign(); } private void printRequestInput(BaseVo baseVo) { HttpServletRequest request = getRequest(); Map map = new LinkedHashMap<>(); map.put("url", request.getRequestURL().toString()); Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = (String) headerNames.nextElement(); map.put(headerName, request.getHeader(headerName)); } // Slf4jLogUtil.info("Request " + toRequestSn(baseVo) + " Info : " + // map); } private void printRequestOutput(BaseVo baseVo, Object result) { // logger.info("Request " + toRequestSn(baseVo) + " Result : " + // result); // Slf4jLogUtil.info("Method = " + getRequest().getRequestURI() // + ", Response Result : " + gson.toJson(result)); } public HttpServletRequest getRequest() { if (RequestContextHolder.getRequestAttributes() == null) { return null; } return ((ServletRequestAttributes) RequestContextHolder .getRequestAttributes()).getRequest(); } }

 然后再请求的方法上加上注解字段@CheckSign

	@CheckSign
	@ApiOperation("生成主密钥km(主密钥合成)")
	@RequestMapping(value = "/makeKMSecretKey", method = RequestMethod.POST)
	public Object makeKMSecretKey(@RequestBody MakeKMSecretKeyReqVo req) {
		ValidatorUtils.validateEntity(req);
		if (Integer.parseInt(req.getType()) != 4
				|| !req.getKeyNumber().equals("1"))
			return ResponseUtils.map("-131",
					i18nUtils.getKey("result.fisherman.numberandtye.error"));
		Slf4jLogUtil
				.info("Method = /encryCard-api/fishermanjec/makeKMSecretKey, "
						+ "Request paramsStr : " + gson.toJson(req));
		Object makeKMSecretKey = fishermanService.makeKMSecretKey(req);
		Slf4jLogUtil
				.info("Method = /encryCard-api/fishermanjec/makeKMSecretKey, "
						+ "Response Result : " + gson.toJson(makeKMSecretKey));
		return makeKMSecretKey;

	}
package com.people.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 参数签名验证注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckSign {

	String value() default "";
}
package com.people.domain.finsherman.vo;

import java.io.Serializable;

import io.swagger.annotations.ApiModelProperty;

import javax.validation.constraints.NotEmpty;

import com.people.domain.BaseVo;

public class MakeKMSecretKeyReqVo extends BaseVo implements Serializable {

	@ApiModelProperty(value = "type")
	@NotEmpty(message = "类型不能为空")
	private String type;
	@ApiModelProperty(value = "keyNumber")
	@NotEmpty(message = "密钥存储编号不能为空")
	private String keyNumber;
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public String getKeyNumber() {
		return keyNumber;
	}
	public void setKeyNumber(String keyNumber) {
		this.keyNumber = keyNumber;
	}
	

}

 

你可能感兴趣的:(数据完整性校验,数据完整性校验,数据校验,参数签名,签名)