【Java】 WebService 校验机制
测试环境域名 |
不可见 |
正式环境域名 |
不可见 |
1.2、安全校验凭证
accessId(授权ID) |
测试/正式待定 |
securityKey(加密密钥) |
测试/正式待定 |
1.3、安全校验机制
1.3.1、在请求头中必须带入的参数
字段 |
类型 |
是否必填 |
入参类型 |
说明 |
accessId |
String |
是 |
header |
dms提供 |
timestamp |
Long |
是 |
header |
时间戳,精确到毫秒级 |
sign |
String |
是 |
header |
算法规则见下说明 |
1.3.2、安全码(sign)生成规则
securityKey+ timestamp +method+uri+参数json字符串(入参使用TreeMap,保证两边参数排序的一致性),使用md5加密;
注释分析代码:
// 校验方法 ElemBean validBean = intfAuth.isValid(request, params, true); /** * 必要信息:accessId * @param request * @param datas * @param checkSign * @return */ public ElemBean isValid(HttpServletRequest request, Map<String, Object> datas, boolean checkSign) { // 创建一个空ElemBean ElemBean ret = new ElemBean(); // 先放了成功的状态 ret.put("stateCode", IntfAuthCode2.SUC.getStateCode()); ret.put("message", IntfAuthCode2.SUC.getMessage()); // 这个是对请求进行了判空处理 ElemBean checkNullBean = checkCruxInfoNull(request, checkSign); if(!checkNullBean.eq("stateCode", IntfAuthCode2.SUC.getStateCode())) { // 空了就返回判空的处理结果 ret.putAll(checkNullBean); return ret; } // 获取安全密钥 String securityKey = getSecurityKey(request.getHeader("accessId")); // 密钥判空处理 if(securityKey == null || "".equals(securityKey)) { ret.put("stateCode", IntfAuthCode2.ACCESS_ID_INVALID.getStateCode()); ret.put("message", IntfAuthCode2.ACCESS_ID_INVALID.getMessage()); return ret; } // 这个判断应该是一个开关,如果false就不做Sign安全码验证 if(checkSign) { // 开始生成安全码 try { String sign = sign(request, datas, securityKey); // 如果我们生成的MD5 和对方提供的不一致,返回非法访问 if(!sign.equals(request.getHeader("sign"))) { ret.put("stateCode", IntfAuthCode2.SIGN_INVALID.getStateCode()); ret.put("message", IntfAuthCode2.SIGN_INVALID.getMessage()); return ret; } } catch (JsonProcessingException e) { logger.error(e.getMessage(), e); ret.put("stateCode", IntfAuthCode2.SIGN_INVALID.getStateCode()); ret.put("message", IntfAuthCode2.SIGN_INVALID.getMessage()); } } return ret; } // 判空方法 ElemBean checkNullBean = checkCruxInfoNull(request, checkSign); private ElemBean checkCruxInfoNull(HttpServletRequest request, boolean checkSign) { ElemBean ret = new ElemBean(); try{ // 先拿出来 accessId String accessId = request.getHeader("accessId"); // 判空 accessId 空就重设状态,表示没有accessId if(accessId == null || "".equals(accessId)) { ret.put("stateCode", IntfAuthCode2.NO_ACCESS_ID.getStateCode()); ret.put("message", IntfAuthCode2.NO_ACCESS_ID.getMessage()); return ret; // 这里都是直接返回结束 } // 判空 sign 空就重设状态,表示没有sign if(checkSign && request.getHeader("sign") == null) { ret.put("stateCode", IntfAuthCode2.NO_SIGN.getStateCode()); ret.put("message", IntfAuthCode2.NO_SIGN.getMessage()); return ret; // 这里都是直接返回结束 } // accessId 和 sign 都有的,就返回成功 ret.put("stateCode", IntfAuthCode2.SUC.getStateCode()); ret.put("message", IntfAuthCode2.SUC.getMessage()); } catch(Exception e){ // 异常就返回 系统异常 ret.put("stateCode", IntfAuthCode2.SYS_ERR.getStateCode()); ret.put("message", IntfAuthCode2.SYS_ERR.getMessage()); logger.error(e.getMessage(), e); } return ret; } // 获取安全密钥 String securityKey = getSecurityKey(request.getHeader("accessId")); private String getSecurityKey(String accessId) { String securityKey = null; try{ // 这里访问了 dcs.interface 库 bt.beginTxn(IntfConstans.TXN_INTF, IntfConstans.JNDI_INTF); // 拿accessId(APP_ID) 找CLEINT_ID 然后返回 TmIntfAuthSecurityPO po = TmIntfAuthSecurityPO.findFirst("APP_ID=? AND VALID_STATUS=10011001", accessId); if(po != null) { securityKey = po.getString("CLEINT_ID"); } bt.endTxnTrue(); } catch(Exception e){ bt.endTxnFalse(); logger.error(e.getMessage(), e); } finally{ bt.txnClean(); } return securityKey; } // 安全码生成规则 String sign = sign(request, datas, securityKey); /** * 1、这里要提供 请求头,所以带了 HttpServletRequest * 2、请求的参数在 datas (requestParam) * 3、从dcs.interface.tm_intf_auth_security 查询的对应的CLEINT_ID(安全密钥) */ public String sign(HttpServletRequest request, Map<String, Object> datas, String securityKey) throws JsonProcessingException { TreeMap<String, Object> treeMap = new TreeMap<String, Object>(); treeMap.putAll(datas); // 先拿了头的时间戳 String timestamp = request.getHeader("timestamp"); ObjectMapper mapper = new ObjectMapper(); StringBuilder signSb = new StringBuilder(); // 安全码(sign)生成规则 securityKey+ timestamp +method+uri+参数json字符串(入参使用TreeMap,保证两边参数排序的一致性),使用md5加密; signSb.append(securityKey).append(timestamp.toString()).append(request.getMethod()).append(request.getRequestURI()).append(mapper.writeValueAsString(treeMap)); // 输出显示看下 logger.info(timestamp+","+signSb.toString()); // 用MD5生成加密码 String sign = DigestUtils.md5Hex(signSb.toString()); logger.info("sign:"+sign); // 返回 return sign; }
校验源码:
package com.yonyou.dcs.intf.common; import java.util.Map; import java.util.TreeMap; import javax.servlet.http.HttpServletRequest; import org.apache.commons.codec.digest.DigestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.yonyou.dcs.common.bean.ElemBean; import com.yonyou.dcs.intf.po.TmIntfAuthSecurityPO; /** * 新的接口安全校验机制 * 现用于:新车商城接口 * @author Administrator * */ @Service public class IntfAuthValid2 { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private BaseTxn bt; /** * 必要信息:accessId * @param request * @param datas * @param checkSign * @return */ public ElemBean isValid(HttpServletRequest request, Map<String, Object> datas, boolean checkSign) { ElemBean ret = new ElemBean(); ret.put("stateCode", IntfAuthCode2.SUC.getStateCode()); ret.put("message", IntfAuthCode2.SUC.getMessage()); ElemBean checkNullBean = checkCruxInfoNull(request, checkSign); if(!checkNullBean.eq("stateCode", IntfAuthCode2.SUC.getStateCode())) { ret.putAll(checkNullBean); return ret; } String securityKey = getSecurityKey(request.getHeader("accessId")); if(securityKey == null || "".equals(securityKey)) { ret.put("stateCode", IntfAuthCode2.ACCESS_ID_INVALID.getStateCode()); ret.put("message", IntfAuthCode2.ACCESS_ID_INVALID.getMessage()); return ret; } if(checkSign) { try { String sign = sign(request, datas, securityKey); if(!sign.equals(request.getHeader("sign"))) { ret.put("stateCode", IntfAuthCode2.SIGN_INVALID.getStateCode()); ret.put("message", IntfAuthCode2.SIGN_INVALID.getMessage()); return ret; } } catch (JsonProcessingException e) { logger.error(e.getMessage(), e); ret.put("stateCode", IntfAuthCode2.SIGN_INVALID.getStateCode()); ret.put("message", IntfAuthCode2.SIGN_INVALID.getMessage()); } } return ret; } public String sign(HttpServletRequest request, Map<String, Object> datas, String securityKey) throws JsonProcessingException { TreeMap<String, Object> treeMap = new TreeMap<String, Object>(); treeMap.putAll(datas); String timestamp = request.getHeader("timestamp"); ObjectMapper mapper = new ObjectMapper(); StringBuilder signSb = new StringBuilder(); signSb.append(securityKey).append(timestamp.toString()).append(request.getMethod()).append(request.getRequestURI()).append(mapper.writeValueAsString(treeMap)); logger.info(timestamp+","+signSb.toString()); String sign = DigestUtils.md5Hex(signSb.toString()); logger.info("sign:"+sign); return sign; } private ElemBean checkCruxInfoNull(HttpServletRequest request, boolean checkSign) { ElemBean ret = new ElemBean(); try{ String accessId = request.getHeader("accessId"); if(accessId == null || "".equals(accessId)) { ret.put("stateCode", IntfAuthCode2.NO_ACCESS_ID.getStateCode()); ret.put("message", IntfAuthCode2.NO_ACCESS_ID.getMessage()); return ret; } if(checkSign && request.getHeader("sign") == null) { ret.put("stateCode", IntfAuthCode2.NO_SIGN.getStateCode()); ret.put("message", IntfAuthCode2.NO_SIGN.getMessage()); return ret; } ret.put("stateCode", IntfAuthCode2.SUC.getStateCode()); ret.put("message", IntfAuthCode2.SUC.getMessage()); } catch(Exception e){ ret.put("stateCode", IntfAuthCode2.SYS_ERR.getStateCode()); ret.put("message", IntfAuthCode2.SYS_ERR.getMessage()); logger.error(e.getMessage(), e); } return ret; } private String getSecurityKey(String accessId) { String securityKey = null; try{ bt.beginTxn(IntfConstans.TXN_INTF, IntfConstans.JNDI_INTF); TmIntfAuthSecurityPO po = TmIntfAuthSecurityPO.findFirst("APP_ID=? AND VALID_STATUS=10011001", accessId); if(po != null) { securityKey = po.getString("CLEINT_ID"); } bt.endTxnTrue(); } catch(Exception e){ bt.endTxnFalse(); logger.error(e.getMessage(), e); } finally{ bt.txnClean(); } return securityKey; } }
相关的状态字典:
1.4、返回码描述
1.4.1、系统相关
返回码 |
描述 |
0 |
成功 |
1.00.000 |
system error,系统错误,联系管理员 |
1.00.001 |
no access id |
1.00.002 |
access id invalid |
1.00.003 |
no sign |
1.00.004 |
sign invalid |
1.00.005 |
数据重复接收 |
|
|
1.4.2、新车商城相关
返回码 |
描述 |
1.01.001 |
vin信息无效 |
1.01.002 |
车辆未实销 |
1.01.003 |
车辆非江铃车 |
1.01.004 |
车辆行驶里程小于进厂里程 |
1.01.005 |
关键字段不能为空 |
1.01.006 |
关键字段值不正确 |
1.01.007 |
车辆不符合产品规则 |
1.01.008 |
车辆已购买过服务包 |
1.01.009 |
维修站无效 |
1.02.001 |
字段值不符合要求 |
1.02.002 |
商城订单号重复 |
1.02.003 |
商品代码不存在 |