秘钥管理改造,服务端返回sign

1.x
tanghc 5 years ago
parent 091845f684
commit 986ad2b34d
  1. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java
  2. 32
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/IsvDefinition.java
  3. 1
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java
  4. 5
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/easyopen/EasyopenZuulConfiguration.java
  5. 113
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
  6. 20
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/SecretContext.java
  7. 27
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java
  8. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipaySignature.java
  9. 9
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java
  10. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreRoutePermissionFilter.java

@ -17,6 +17,12 @@ public interface Isv {
*/
String getSecretInfo();
/**
* 获取平台的私钥
* @return 返回私钥
*/
String getPrivateKeyPlatform();
/**
* 0启用1禁用
* @return 返回状态

@ -2,9 +2,6 @@ package com.gitee.sop.gatewaycommon.bean;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
/**
* @author tanghc
@ -13,6 +10,8 @@ import java.util.Date;
@Setter
public class IsvDefinition implements Isv {
public static final int SIGN_TYPE_RSA2 = 1;
public IsvDefinition() {
}
@ -21,35 +20,38 @@ public class IsvDefinition implements Isv {
this.secret = secret;
}
private Long id;
private String appKey;
/** 秘钥,如果是支付宝开放平台,对应的pubKey */
/** 秘钥,签名方式为MD5时有用 */
private String secret;
private String pubKey;
/** 开发者生成的公钥, 数据库字段:public_key_isv */
private String publicKeyIsv;
/** 平台生成的私钥, 数据库字段:private_key_platform */
private String privateKeyPlatform;
/** 0启用,1禁用 */
private Byte status;
private Date gmtCreate;
private Date gmtModified;
/** 签名类型:1:RSA2,2:MD5 */
private Byte signType = 1;
@Override
public String getSecretInfo() {
return StringUtils.isBlank(pubKey) ? secret : pubKey;
return signType == SIGN_TYPE_RSA2 ? publicKeyIsv : secret;
}
@Override
public String toString() {
return "IsvDefinition{" +
"id=" + id +
", appKey='" + appKey + '\'' +
"appKey='" + appKey + '\'' +
", secret='" + secret + '\'' +
", publicKeyIsv='" + publicKeyIsv + '\'' +
", privateKeyPlatform='" + privateKeyPlatform + '\'' +
", status=" + status +
", gmtCreate=" + gmtCreate +
", gmtModified=" + gmtModified +
", signType=" + signType +
'}';
}
}

@ -11,6 +11,7 @@ public class SopConstants {
private SopConstants() {}
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
public static final String UTF8 = "UTF-8";
public static final String FORMAT_JSON = "json";
public static final String DEFAULT_SIGN_METHOD = "md5";
public static final String EMPTY_JSON = "{}";

@ -3,7 +3,6 @@ package com.gitee.sop.gatewaycommon.easyopen;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.secret.SecretContext;
import com.gitee.sop.gatewaycommon.zuul.configuration.BaseZuulConfiguration;
/**
@ -11,10 +10,6 @@ import com.gitee.sop.gatewaycommon.zuul.configuration.BaseZuulConfiguration;
*/
public class EasyopenZuulConfiguration extends BaseZuulConfiguration {
static {
SecretContext.setSecretGetter((isvDefinition)-> isvDefinition.getSecret());
}
public EasyopenZuulConfiguration() {
ApiConfig apiConfig = ApiContext.getApiConfig();
if (compatibilityModel()) {

@ -7,17 +7,23 @@ import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
import com.gitee.sop.gatewaycommon.bean.ErrorDefinition;
import com.gitee.sop.gatewaycommon.bean.Isv;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.secret.IsvManager;
import com.gitee.sop.gatewaycommon.validate.alipay.AlipayConstants;
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySignature;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.BooleanUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.Map;
@ -26,6 +32,7 @@ import java.util.Optional;
/**
* @author tanghc
*/
@Slf4j
public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R> {
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta();
@ -36,6 +43,7 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
public static final String ARRAY_START = "[";
public static final String ARRAY_END = "]";
public static final String ROOT_JSON = "{'items':%s}".replace("'", "\"");
public static final String ERROR_METHOD = "error";
/**
@ -70,33 +78,33 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
}
serviceResult = wrapResult(serviceResult);
int responseStatus = this.getResponseStatus(request);
JSONObject jsonObjectService;
JSONObject responseData;
if (responseStatus == HttpStatus.OK.value()) {
// 200正常返回
jsonObjectService = JSON.parseObject(serviceResult);
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
responseData = JSON.parseObject(serviceResult);
responseData.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
responseData.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
} else if (responseStatus == SopConstants.BIZ_ERROR_STATUS) {
// 如果是业务出错
this.storeError(request, ErrorType.BIZ);
jsonObjectService = JSON.parseObject(serviceResult);
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg());
responseData = JSON.parseObject(serviceResult);
responseData.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode());
responseData.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg());
} else {
this.storeError(request, ErrorType.UNKNOWN);
// 微服务端有可能返回500错误
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
jsonObjectService = new JSONObject();
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
responseData = new JSONObject();
responseData.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
responseData.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
}
return this.merge(request, jsonObjectService);
return this.merge(request, responseData);
}
/**
* 保存错误信息
*
* @param request
* @param request request
*/
protected void storeError(T request, ErrorType errorType) {
ApiInfo apiInfo = this.getApiInfo(request);
@ -116,7 +124,7 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
/**
* 该路由是否合并结果
*
* @param request
* @param request request
* @return true需要合并
*/
protected boolean isMergeResult(T request) {
@ -137,13 +145,8 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
protected ApiInfo getApiInfo(T request) {
Map<String, Object> params = this.getApiParam(request);
String name = Optional.ofNullable(params)
.map(map -> (String) map.get(ParamNames.API_NAME))
.orElse("method.unknown");
String version = Optional.ofNullable(params)
.map(map -> (String) map.get(ParamNames.VERSION_NAME))
.orElse("version.unknown");
String name = this.getParamValue(params, ParamNames.API_NAME, "method.unknown");
String version = this.getParamValue(params, ParamNames.VERSION_NAME, "version.unknown");
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(name + version);
@ -178,47 +181,67 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
return serviceResult;
}
public String merge(T exchange, JSONObject jsonObjectService) {
JSONObject ret = new JSONObject();
String name = "error";
public String merge(T exchange, JSONObject responseData) {
JSONObject finalData = new JSONObject();
Map<String, Object> params = this.getApiParam(exchange);
if (params != null) {
Object method = params.get(ParamNames.API_NAME);
if (method != null) {
name = String.valueOf(method);
}
}
String name = this.getParamValue(params, ParamNames.API_NAME, ERROR_METHOD);
ApiConfig apiConfig = ApiConfig.getInstance();
// 点换成下划线
DataNameBuilder dataNameBuilder = apiConfig.getDataNameBuilder();
String method = dataNameBuilder.build(name);
ret.put(method, jsonObjectService);
this.appendReturnSign(apiConfig, params, ret);
// alipay_goods_get_response
String responseDataNodeName = dataNameBuilder.build(name);
finalData.put(responseDataNodeName, responseData);
ResultAppender resultAppender = apiConfig.getResultAppender();
// 追加额外的结果
if (resultAppender != null) {
resultAppender.append(ret, params, exchange);
resultAppender.append(finalData, params, exchange);
}
return ret.toJSONString();
// 添加服务端sign
if (apiConfig.isShowReturnSign() && !CollectionUtils.isEmpty(params)) {
// 添加try...catch,生成sign出错不影响结果正常返回
try {
String sign = this.createResponseSign(apiConfig, params, responseData.toJSONString());
if (StringUtils.hasLength(sign)) {
finalData.put(ParamNames.SIGN_NAME, sign);
}
protected void appendReturnSign(ApiConfig apiConfig, Map<String, ?> params, JSONObject ret) {
if (apiConfig.isShowReturnSign() && params != null) {
Object appKey = params.get(ParamNames.APP_KEY_NAME);
String sign = this.createReturnSign(String.valueOf(appKey));
ret.put(ParamNames.SIGN_NAME, sign);
} catch (Exception e) {
log.error("生成平台签名失败, params: {}, serviceResult:{}", JSON.toJSONString(params), responseData, e);
}
}
return finalData.toJSONString();
}
protected String getParamValue(Map<String, Object> apiParam, String key, String defaultValue) {
return CollectionUtils.isEmpty(apiParam) ? defaultValue : (String) apiParam.getOrDefault(key, defaultValue);
}
/**
* 这里需要使用平台的私钥生成一个sign需要配置两套公私钥目前暂未实现
* 这里需要使用平台的私钥生成一个sign需要配置两套公私钥
*
* @param appKey
* @return
* @param apiConfig 配置
* @param params 请求参数
* @param serviceResult 业务返回结果
* @return 返回平台生成的签名
*/
protected String createReturnSign(String appKey) {
// TODO: 返回sign
protected String createResponseSign(ApiConfig apiConfig, Map<String, Object> params, String serviceResult) {
IsvManager isvManager = apiConfig.getIsvManager();
// 根据appId获取秘钥
String appKey = this.getParamValue(params, ParamNames.APP_KEY_NAME, "");
if (StringUtils.isEmpty(appKey)) {
return null;
}
Isv isvInfo = isvManager.getIsv(appKey);
String privateKeyPlatform = isvInfo.getPrivateKeyPlatform();
if (StringUtils.isEmpty(privateKeyPlatform)) {
return null;
}
String charset = Optional.ofNullable(params.get(ParamNames.CHARSET_NAME))
.map(String::valueOf)
.orElse(SopConstants.UTF8);
String signType = getParamValue(params, ParamNames.SIGN_TYPE_NAME, AlipayConstants.SIGN_TYPE_RSA2);
return AlipaySignature.rsaSign(serviceResult, privateKeyPlatform, charset, signType);
}
@Getter
@Setter

@ -1,20 +0,0 @@
package com.gitee.sop.gatewaycommon.secret;
import com.gitee.sop.gatewaycommon.bean.IsvDefinition;
import java.util.function.Function;
/**
* @author tanghc
*/
public class SecretContext {
private static volatile Function<IsvDefinition, String> secretGetter = (isv) -> isv.getPubKey();
public static Function<IsvDefinition, String> getSecretGetter() {
return secretGetter;
}
public static void setSecretGetter(Function<IsvDefinition, String> secretGetter) {
SecretContext.secretGetter = secretGetter;
}
}

@ -2,8 +2,11 @@ package com.gitee.sop.gatewaycommon.validate;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import com.gitee.sop.gatewaycommon.bean.Isv;
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
@ -12,6 +15,7 @@ import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.param.UploadContext;
import com.gitee.sop.gatewaycommon.secret.IsvManager;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
@ -57,6 +61,7 @@ public class ApiValidator implements Validator {
checkTimeout(param);
checkFormat(param);
checkUploadFile(param);
checkPermission(param);
}
/**
@ -156,9 +161,8 @@ public class ApiValidator implements Validator {
throw ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG.getErrorMeta().getException();
}
Signer signer = apiConfig.getSigner();
boolean isRightSign = signer.checkSign(param, secret);
// 错误的sign
if (!isRightSign) {
if (!signer.checkSign(param, secret)) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion());
}
} finally {
@ -177,4 +181,23 @@ public class ApiValidator implements Validator {
}
}
/**
* 校验访问权限
* @param apiParam
*/
protected void checkPermission(ApiParam apiParam) {
String routeId = apiParam.fetchNameVersion();
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(routeId);
BaseRouteDefinition routeDefinition = targetRoute.getRouteDefinition();
boolean needCheckPermission = BooleanUtils.toBoolean(routeDefinition.getPermission());
if (needCheckPermission) {
IsvRoutePermissionManager isvRoutePermissionManager = ApiConfig.getInstance().getIsvRoutePermissionManager();
String appKey = apiParam.fetchAppKey();
boolean hasPermission = isvRoutePermissionManager.hasPermission(appKey, routeId);
if (!hasPermission) {
throw ErrorEnum.ISV_ROUTE_NO_PERMISSIONS.getErrorMeta().getException();
}
}
}
}

@ -88,16 +88,16 @@ public class AlipaySignature {
* sha256WithRsa 加签
*
* @param content
* @param publicKey
* @param privateKey
* @param charset
* @return
*/
public static String rsa256Sign(String content, String publicKey,
public static String rsa256Sign(String content, String privateKey,
String charset) {
try {
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA,
new ByteArrayInputStream(publicKey.getBytes()));
new ByteArrayInputStream(privateKey.getBytes()));
java.security.Signature signature = java.security.Signature
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS);

@ -7,7 +7,6 @@ import com.gitee.sop.gatewaycommon.zuul.filter.ErrorFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.FormBodyWrapperFilterExt;
import com.gitee.sop.gatewaycommon.zuul.filter.PostResultFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PreLimitFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PreRoutePermissionFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PreValidateFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.Servlet30WrapperFilterExt;
import com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator;
@ -96,14 +95,6 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
return new PreLimitFilter();
}
/**
* 权限校验
*/
@Bean
PreRoutePermissionFilter preRoutePermissionFilter() {
return new PreRoutePermissionFilter();
}
/**
* 错误处理扩展
*/

@ -15,7 +15,9 @@ import org.apache.commons.lang3.BooleanUtils;
/**
* 路由权限校验有些接口需要配置权限才能访问
* @author tanghc
* @deprecated 已经整合到ApiValidator中见ApiValidator.checkPermission()
*/
@Deprecated
public class PreRoutePermissionFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {

Loading…
Cancel
Save