校验数据完整性

  • Post author:
  • Post category:其他


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

名称

字段

类型

是否必须

数据校验码

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<String, String> parameters) {
		StringBuffer sb = new StringBuffer();
		for (Map.Entry<String, String> 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<Field> 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<String, String> initQueryInfoMap(SceneNameVo vo) {
	  Map<String, String> map = new HashMap<String, String>();
	  map.put("custNumber", vo.getCustNumber());
	  map.put("sceneName", vo.getSceneName());
	  return new TreeMap<>(map);
	 }
	 /**
	  * 初始化请求参数
	  * 
	  * @param vo
	  * @return
	  */
	 public static SortedMap<String, String> initQueryInfoMap(SourceDataVo vo) {
	  Map<String, String> map = new HashMap<String, String>();
	  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<Map<String, String>> list=new ArrayList<Map<String, String>>();
		
		SortedMap<String, String> map = new TreeMap<String, String>();
//		map.put("queryId", "1");
		Map<String, String> params = new HashMap<String, String>();
		params.put("idCard", "640651354422655445");
		params.put("userName", "zzq");
		list.add(params);
		Map<String, String> params2 = new HashMap<String, String>();
		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<String, String> params22 = new HashMap<String, String>();
//		params22.put("queryId", "1");
//		params22.put("sign", sign);
//		String json2 = gson.toJson(params22);
//		System.out.println(json2);
	
	

	}
	 
	  
}

列子:

客户端请求

// 获取客户编号
			InterfaceInfoVo selectInfoKey = interfaceInfoDao.selectInfoKey();
			// 调用服务端接口
			Map<String, Object> params = new HashMap<String, Object>();
			params.put("cipherInfo", Long.parseLong(appId));
			params.put("queryName", selectInfoKey.getCustNumber());
			SortedMap<String, String> map = new TreeMap<String, String>();
			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<String, String> map = new HashMap<String, String>();
		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字是否合法<br>
	 * 若不合法,停止接口调用并且返回错误消息
	 */
	@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<String, String> 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;
	}
	

}



版权声明:本文为weixin_38501485原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。