Authored by caoyan

验签

... ... @@ -251,5 +251,10 @@
<groupId>com.yoho.ufo.model</groupId>
<artifactId>order-ufo-model</artifactId>
</dependency>
<dependency>
<groupId>com.yoho.secu</groupId>
<artifactId>sign</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
... ...
package com.yohoufo.common.controller;
import com.alibaba.fastjson.JSONObject;
import com.netflix.config.DynamicPropertyFactory;
import com.yoho.core.common.utils.MD5;
import com.yoho.tools.docs.*;
import com.yohoufo.common.ApiResponse;
import com.yohoufo.common.annotation.IgnoreSession;
import com.yohoufo.common.annotation.IgnoreSignature;
import com.yohoufo.common.cache.Cachable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
... ... @@ -15,13 +7,25 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.yoho.signature.SignKey;
import com.yoho.tools.docs.Api;
import com.yoho.tools.docs.ApiGroup;
import com.yoho.tools.docs.ApiOperation;
import com.yoho.tools.docs.ApiParam;
import com.yoho.tools.docs.ApiRespCode;
import com.yohoufo.common.ApiResponse;
import com.yohoufo.common.annotation.IgnoreSession;
import com.yohoufo.common.annotation.IgnoreSignature;
import com.yohoufo.common.cache.Cachable;
@Api(name = "配置项",desc="配置项")
@ApiGroup(name="resources",desc = "resources")
@RestController
public class ConfigController {
private Logger logger = LoggerFactory.getLogger(ConfigController.class);
/**
* 获取验签秘钥接口
*
... ... @@ -35,14 +39,11 @@ public class ConfigController {
@RequestMapping(params = "method=ufo.simple.pice")
@ResponseBody
@Cachable(expire = 60)
public ApiResponse getSignatureKey(@RequestParam("udid") String udid) {
//获取md5盐值
String md5Salt = DynamicPropertyFactory.getInstance().getStringProperty("gateway.signature.key.salt", "mQyMTMwZjlmZTZmYjY4UjkNmYwZGM0OTk0Y").get();
String encryptStr = udid + md5Salt;
//对udid加盐后二次md5
String signatureKey = MD5.md5(MD5.md5(encryptStr));
JSONObject data = new JSONObject();
data.put("sk", signatureKey);
public ApiResponse getSignatureKey(@RequestParam("udid") String udid,
@RequestParam(value = "app_version", required = false, defaultValue = "6.9.13") String appVersion) {
JSONObject data = new JSONObject();
data.put("sk", SignKey.getSK(udid));
data.put("tc", SignKey.getTC(udid, appVersion));
return new ApiResponse.ApiResponseBuilder().code(200).message("success").data(data).build();
}
... ...
package com.yohoufo.common.interceptor;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import com.yoho.core.config.ConfigReader;
import com.yoho.core.rabbitmq.YhProducer;
... ... @@ -10,34 +35,12 @@ import com.yoho.core.rest.client.ServiceCaller;
import com.yoho.error.event.LogEvent;
import com.yoho.service.model.request.SessionFailedBO;
import com.yoho.service.model.request.UserSessionReqBO;
import com.yoho.signature.MsgSign;
import com.yohoufo.common.annotation.IgnoreSession;
import com.yohoufo.common.exception.GatewayException;
import com.yohoufo.common.exception.SessionExpireException;
import com.yohoufo.common.exception.VersionNotSupportException;
import com.yohoufo.common.utils.ServletUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class SecurityInterceptor implements HandlerInterceptor, ApplicationEventPublisherAware {
... ... @@ -110,14 +113,12 @@ public class SecurityInterceptor implements HandlerInterceptor, ApplicationEvent
String businessLine = params.get("business_line");
//==============以下是完全不校验的场景=========================
//1 后门, 不需要校验, (1)检查请求参数是否有预留的后门参数_sncp, 并且_sncp的值是有效的. 放行 _sncp的值再5.6版本会去掉
String temporaryValueQeq = params.get("_sncp"); //请求的预留后门的值
String temporaryValue = configReader.getString("gateway.client.secret.h5", "");
if (!StringUtils.isEmpty(temporaryValueQeq) && !StringUtils.isEmpty(temporaryValue) && temporaryValueQeq.trim().equals(temporaryValue.trim()) && isInnerIp(httpServletRequest)){
return ;
//1 验证参数
MsgSign msgSign = new MsgSign();
if (msgSign.paramVerify(params) && isInnerIp(httpServletRequest)){
return;
}
//2 是否校验全部接口,开关-true:校验全部接口(除去@IgnoreSession注解接口) 开关-false:只校验核心接口
boolean isVerifyAllMethod = configReader.getBoolean("gateway.session.isVerifyAllMethod", true);
if(!isVerifyAllMethod){
... ...
package com.yohoufo.common.interceptor;
import com.google.common.collect.ImmutableList;
import com.netflix.config.DynamicPropertyFactory;
import com.yoho.core.common.utils.MD5;
import com.yoho.core.config.ConfigReader;
import com.yoho.error.event.LogEvent;
import com.yohoufo.common.annotation.IgnoreSignature;
import com.yohoufo.common.exception.SignatureNotMatchException;
import com.yohoufo.common.utils.MD5Util;
import com.yohoufo.common.utils.ServletUtils;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
... ... @@ -18,23 +21,17 @@ import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import com.yoho.core.config.ConfigReader;
import com.yoho.error.event.LogEvent;
import com.yoho.signature.MsgSign;
import com.yohoufo.common.annotation.IgnoreSignature;
import com.yohoufo.common.exception.SignatureNotMatchException;
import com.yohoufo.common.utils.ServletUtils;
public class SignatureVerifyInterceptor implements HandlerInterceptor, ApplicationEventPublisherAware {
private final Logger logger = LoggerFactory.getLogger(SignatureVerifyInterceptor.class);
//验签算法
private final static String VERIFY_ARITHMETIC = "HmacSHA256";
//客户端加签请求头
private final static String HTTP_HEADER_VERIFY_DATA = "x-yoho-verify";
... ... @@ -74,20 +71,18 @@ public class SignatureVerifyInterceptor implements HandlerInterceptor, Applicati
if(this.validateReqParams(httpServletRequest, params)){
return true;
}
//(4) 预留后门, 如果是内网并且传入_sncp参数, 并且验证成功, 不验证.
String temporaryValueQeq = params.get("_sncp"); //请求的预留后门的值
String temporaryValue = configReader.getString("gateway.client.secret.h5", "");
logger.debug("SignatureVerifyInterceptor. method is {}, temporaryValueQeq is {}, temporaryValue is {} and isInner is {}", method, temporaryValueQeq, temporaryValue);
if (!StringUtils.isEmpty(temporaryValueQeq) && !StringUtils.isEmpty(temporaryValue) && temporaryValueQeq.trim().equals(temporaryValue.trim()) && isInnerIp(httpServletRequest)){
//(4) 验证参数
MsgSign msgSign = new MsgSign();
if (msgSign.paramVerify(params) && isInnerIp(httpServletRequest,false)){
return true;
}
//(4) 验证消息签名.
String message = this.getPramsString(params);
String message = msgSign.getPramsString(params);
String verifyMessage = httpServletRequest.getHeader(HTTP_HEADER_VERIFY_DATA);
logger.debug("verifySignature: message is {}, verifyMessage is {}", message, verifyMessage);
boolean verifyRes = this.verifySignatureNew(udid, message, verifyMessage);
boolean verifyRes = msgSign.verifySign(udid, message, verifyMessage, false, appVersion);
logger.debug("verifySignature: message is {}, verifyMessage is {}, res is {}", message, verifyMessage, verifyRes);
if(!verifyRes){
... ... @@ -172,7 +167,7 @@ public class SignatureVerifyInterceptor implements HandlerInterceptor, Applicati
private boolean validateReqParams(HttpServletRequest request, Map<String, String> params){
//内网访问的pc/h5不作校验
String clientType = params.get("client_type");
if(("web".equals(clientType) || "h5".equals(clientType) || "wechat".equals(clientType)) && isInnerIp(request)){
if(("web".equals(clientType) || "h5".equals(clientType) || "wechat".equals(clientType)) && isInnerIp(request, true)){
return true;
}
//是否校验全部接口,开关-true:校验全部接口(除含@IgnoreSignature注解接口) 开关-false:只校验核心接口
... ... @@ -193,12 +188,13 @@ public class SignatureVerifyInterceptor implements HandlerInterceptor, Applicati
/**
* 是否内网ip
* @param request
* @param 是否强制验证
* @return
*/
public boolean isInnerIp(HttpServletRequest request){
public boolean isInnerIp(HttpServletRequest request, boolean enFlag){
//增加内网ip验证开关,供压测时使用
boolean isInnerIpVerifyEnable = configReader.getBoolean("gateway.security.isInnerIpVerifyEnable", true);
if(!isInnerIpVerifyEnable){
if(!isInnerIpVerifyEnable && !enFlag){
return true;
}
String ip = getRemoteIP( request );
... ... @@ -243,31 +239,6 @@ public class SignatureVerifyInterceptor implements HandlerInterceptor, Applicati
return ServletUtils.getRequestParams(httpServletRequest);
}
/**
* 拼接请求参数,k1=v1&k2=v2
* @param reqParams
* @return
*/
private String getPramsString(Map<String, String> reqParams) {
ImmutableList list = ImmutableList.of();
SortedMap<String, String> filtedMap = new TreeMap<>();
for (Map.Entry<String, String> entry : reqParams.entrySet()) {
String k = entry.getKey();
if (!list.contains(k)) {
filtedMap.put(k, entry.getValue());
}
}
//string: k1=v1&k2=v2
List<String> array = new LinkedList<>();
for (Map.Entry<String, String> entry : filtedMap.entrySet()) {
String pair = entry.getKey() + "=" + entry.getValue();
array.add(pair.trim());
}
String paramStr = String.join("&", array);
logger.debug("getRequestInfo: param str is: {}", paramStr);
return paramStr;
}
/**
* 验证签名
... ... @@ -276,25 +247,25 @@ public class SignatureVerifyInterceptor implements HandlerInterceptor, Applicati
* @param verifyMessage
* @return
*/
private boolean verifySignatureNew(String udid, String message, String verifyMessage){
//(1) 验证信息为空或者udid为空,直接返回
if(StringUtils.isEmpty(verifyMessage) || StringUtils.isEmpty(udid)){
return false;
}
//(2) 获取秘钥
String md5Salt = DynamicPropertyFactory.getInstance().getStringProperty("gateway.signature.key.salt", "mQyMTMwZjlmZTZmYjY4UjkNmYwZGM0OTk0Y").get();
String encryptStr = udid + md5Salt;
//对udid加盐后二次md5
String signatureKey = MD5.md5(MD5.md5(encryptStr));
//(3) 对消息加签
String signatureRes = this.HmacSHA256(message, signatureKey);
if(verifyMessage.equals(signatureRes)){
return true;
}
logger.warn("verifySignatureNew: signature verify failed: message is {}, verifyMessage is {}, udid is {}, key is {}", message, verifyMessage, udid, signatureKey);
return false;
}
// private boolean verifySignatureNew(String udid, String message, String verifyMessage){
// //(1) 验证信息为空或者udid为空,直接返回
// if(StringUtils.isEmpty(verifyMessage) || StringUtils.isEmpty(udid)){
// return false;
// }
// //(2) 获取秘钥
// String md5Salt = DynamicPropertyFactory.getInstance().getStringProperty("gateway.signature.key.salt", "mQyMTMwZjlmZTZmYjY4UjkNmYwZGM0OTk0Y").get();
// String encryptStr = udid + md5Salt;
// //对udid加盐后二次md5
// String signatureKey = MD5.md5(MD5.md5(encryptStr));
//
// //(3) 对消息加签
// String signatureRes = this.HmacSHA256(message, signatureKey);
// if(verifyMessage.equals(signatureRes)){
// return true;
// }
// logger.warn("verifySignatureNew: signature verify failed: message is {}, verifyMessage is {}, udid is {}, key is {}", message, verifyMessage, udid, signatureKey);
// return false;
// }
/**
* 签名失败上报事件
... ... @@ -312,26 +283,26 @@ public class SignatureVerifyInterceptor implements HandlerInterceptor, Applicati
}
}
/**
* 使用hmac加密
* @param data
* @param secret
* @return
*/
private String HmacSHA256(String data, String secret){
String res = null;
try{
Mac sha256_HMAC = Mac.getInstance(VERIFY_ARITHMETIC);
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), VERIFY_ARITHMETIC);
sha256_HMAC.init(secret_key);
byte[] encryptByte = sha256_HMAC.doFinal(data.getBytes());
res = MD5Util.byteArrayToHexString(encryptByte);
logger.debug("HmacSHA256: data is {}, secret is {}, res is {}", data, secret, res);
}catch (Exception e){
logger.warn("HmacSHA256: hmac failed, data is {}, error is {}", data, e);
}
return res;
}
// /**
// * 使用hmac加密
// * @param data
// * @param secret
// * @return
// */
// private String HmacSHA256(String data, String secret){
// String res = null;
// try{
// Mac sha256_HMAC = Mac.getInstance(VERIFY_ARITHMETIC);
// SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), VERIFY_ARITHMETIC);
// sha256_HMAC.init(secret_key);
// byte[] encryptByte = sha256_HMAC.doFinal(data.getBytes());
// res = MD5Util.byteArrayToHexString(encryptByte);
// logger.debug("HmacSHA256: data is {}, secret is {}, res is {}", data, secret, res);
// }catch (Exception e){
// logger.warn("HmacSHA256: hmac failed, data is {}, error is {}", data, e);
// }
// return res;
// }
//含忽略签名校验注解的接口,不验证
private boolean isVerifySignature(HandlerMethod handlerMethod, String clientType, String appVersion){
... ...