返回sign处理

This commit is contained in:
tanghc
2019-06-27 19:20:23 +08:00
parent 800f7e8d82
commit eb179632c8
22 changed files with 472 additions and 238 deletions

View File

@@ -11,6 +11,7 @@ using SDKCSharp.Request;
using SDKCSharp.Response;
using SDKCSharp.Utility;
using System.IO;
using Newtonsoft.Json.Linq;
namespace SDKCSharp.Client
{
@@ -21,7 +22,6 @@ namespace SDKCSharp.Client
{
private static OpenConfig DEFAULT_CONFIG = new OpenConfig();
private const string ERROR_RESPONSE_KEY = "error_response";
private Dictionary<string, string> header = new Dictionary<string, string>();
@@ -29,22 +29,37 @@ namespace SDKCSharp.Client
private string url;
private string appId;
private string privateKey;
private string publicKeyPlatform;
private OpenConfig openConfig;
private OpenRequest openRequest;
private DataNameBuilder dataNameBuilder;
public OpenClient(string url, string appId, string privateKey) : this(url, appId, privateKey,false, DEFAULT_CONFIG)
public OpenClient(string url, string appId, string privateKey)
: this(url, appId, privateKey,false, DEFAULT_CONFIG)
{
}
public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile) : this(url, appId, privateKey, priKeyFromFile, DEFAULT_CONFIG)
public OpenClient(string url, string appId, string privateKey, string publicKeyPlatform)
: this(url, appId, privateKey)
{
this.publicKeyPlatform = publicKeyPlatform;
}
public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile)
: this(url, appId, privateKey, priKeyFromFile, DEFAULT_CONFIG)
{
}
public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile, string publicKeyPlatform)
: this(url, appId, privateKey, priKeyFromFile)
{
this.publicKeyPlatform = publicKeyPlatform;
}
public OpenClient(string url, string appId, string privateKey,bool priKeyFromFile, OpenConfig openConfig)
{
this.url = url;
@@ -60,6 +75,12 @@ namespace SDKCSharp.Client
this.dataNameBuilder = openConfig.DataNameBuilder;
}
public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile, string publicKeyPlatform, OpenConfig openConfig)
: this(url, appId, privateKey, priKeyFromFile, openConfig)
{
this.publicKeyPlatform = publicKeyPlatform;
}
/// <summary>
/// 加载秘钥文件
/// </summary>
@@ -106,7 +127,7 @@ namespace SDKCSharp.Client
form[this.openConfig.AccessTokenName] = accessToken;
}
form[this.openConfig.AppKeyName] = this.appId;
string sign = SignUtil.CreateSign(form, privateKey, request.Charset, request.SignType);
string sign = SignUtil.CreateSign(form, privateKey, openConfig.Charset, openConfig.SignType);
form[this.openConfig.SignName] = sign;
string resp = this.DoExecute(url, requestForm, header);
@@ -133,22 +154,84 @@ namespace SDKCSharp.Client
/// <param name="resp">服务器响应内容</param>
/// <param name="request">请求Request</param>
/// <returns>返回Response</returns>
protected virtual T ParseResponse<T>(string resp, BaseRequest<T> request) where T: BaseResponse {
protected virtual T ParseResponse<T>(string resp, BaseRequest<T> request) where T: BaseResponse
{
string method = request.Method;
string dataName = this.dataNameBuilder.Build(method);
Dictionary<string, object> jsonObject = JsonUtil.ParseToDictionary(resp);
bool errorResponse = jsonObject.ContainsKey(ERROR_RESPONSE_KEY);
string rootNodeName = this.dataNameBuilder.Build(method);
string errorRootNode = openConfig.ErrorResponseName;
Dictionary<string, object> responseData = JsonUtil.ParseToDictionary(resp);
bool errorResponse = responseData.ContainsKey(errorRootNode);
if (errorResponse)
{
dataName = ERROR_RESPONSE_KEY;
rootNodeName = errorRootNode;
}
object data = jsonObject[dataName];
string jsonData = data == null ? "{}" : data.ToString();
object data = responseData[rootNodeName];
responseData.TryGetValue(openConfig.SignName, out object sign);
if (sign != null && !string.IsNullOrEmpty(publicKeyPlatform))
{
string signContent = BuildBizJson(rootNodeName, resp);
if (!CheckResponseSign(signContent, sign.ToString(), publicKeyPlatform))
{
ErrorResponse checkSignErrorResponse = SopSdkErrors.CHECK_RESPONSE_SIGN_ERROR;
data = JsonUtil.ToJSONString(checkSignErrorResponse);
}
}
string jsonData = data == null ? "{}" : data.ToString();
T t = JsonUtil.ParseObject<T>(jsonData);
t.Body = jsonData;
return t;
}
/// <summary>
/// 验证服务端返回的sign
/// </summary>
/// <returns><c>true</c>, if response sign was checked, <c>false</c> otherwise.</returns>
/// <param name="signContent">Response data.</param>
/// <param name="sign">sign data.</param>
/// <param name="publicKeyPlatform">Public key platform.</param>
protected virtual bool CheckResponseSign(string signContent, string sign, string publicKeyPlatform)
{
try
{
Encoding charset = openConfig.Charset;
SignType signType = openConfig.SignType;
return SignUtil.RsaCheck(signContent, sign, publicKeyPlatform, charset, signType);
}
catch (Exception)
{
return false;
}
}
protected virtual string BuildBizJson(string rootNodeName, string body)
{
int indexOfRootNode = body.IndexOf(rootNodeName);
if (indexOfRootNode < 0)
{
rootNodeName = openConfig.ErrorResponseName;
indexOfRootNode = body.IndexOf(rootNodeName);
}
string result = null;
if (indexOfRootNode > 0)
{
result = BuildJsonNodeData(body, rootNodeName, indexOfRootNode);
}
return result;
}
protected virtual string BuildJsonNodeData(string body, string rootNode, int indexOfRootNode)
{
int signDataStartIndex = indexOfRootNode + rootNode.Length + 2;
int indexOfSign = body.IndexOf("\"" + openConfig.SignName + "\"");
if (indexOfSign < 0)
{
return null;
}
int signDataEndIndex = indexOfSign - 1;
int length = signDataEndIndex - signDataStartIndex;
return body.Substring(signDataStartIndex, length);
}
}
}

View File

@@ -13,9 +13,6 @@ namespace SDKCSharp.Client
{
public class OpenRequest
{
private const string HTTP_ERROR_CODE = "-400";
private OpenConfig openConfig;
private OpenHttp openHttp;
@@ -77,17 +74,10 @@ namespace SDKCSharp.Client
protected string CauseException(Exception e)
{
ErrorResponse result = new ErrorResponse();
result.SubCode = HTTP_ERROR_CODE;
result.SubMsg = e.Message;
result.Code = HTTP_ERROR_CODE;
result.Msg = e.Message;
ErrorResponse result = SopSdkErrors.HTTP_ERROR;
return JsonUtil.ToJSONString(result);
}
}
class ErrorResponse : BaseResponse
{
}
}

View File

@@ -10,7 +10,7 @@ namespace SDKCSharp.Common
private const char DOT = '.';
private const char UNDERLINE = '_';
private const string DATA_SUFFIX = "_response";
private const string DATA_SUFFIX = SopSdkConstants.DATA_SUFFIX;
public string Build(string method)
{

View File

@@ -0,0 +1,8 @@
using System;
namespace SDKCSharp.Common
{
public class ErrorResponse: Response.BaseResponse
{
}
}

View File

@@ -5,192 +5,132 @@ using System.Text;
using System.Threading.Tasks;
using SDKCSharp;
using SDKCSharp.Utility;
namespace SDKCSharp.Common
{
public class OpenConfig
{
private String successCode = SdkConfig.SUCCESS_CODE;
public static DataNameBuilder DATA_NAME_BUILDER = new DefaultDataNameBuilder();
/// <summary>
/// 返回码成功值
/// </summary>
public String SuccessCode
{
get { return successCode; }
set { successCode = value; }
}
public string SuccessCode { get; set; } = "10000";
private String defaultVersion = SdkConfig.DEFAULT_VERSION;
/// <summary>
/// 默认版本号
/// </summary>
public String DefaultVersion
{
get { return defaultVersion; }
set { defaultVersion = value; }
}
public string DefaultVersion { get; set; } = "1.0";
/// <summary>
/// 字符编码
/// </summary>
/// <value>The charset.</value>
public Encoding Charset { get; set; } = Encoding.UTF8;
/// <summary>
/// 签名类型
/// </summary>
/// <value>The type of the sign.</value>
public SignType SignType { get; set; } = SignType.RSA2;
/// <summary>
/// 格式类型
/// </summary>
public string FormatType { get; set; } = "json";
/// <summary>
/// 时间戳格式
/// </summary>
public string TimestampPattern { get; set; } = "yyyy-MM-dd HH:mm:ss";
private String methodName = "method";
/// <summary>
/// 接口属性名
/// </summary>
public String MethodName
{
get { return methodName; }
set { methodName = value; }
}
public string MethodName { get; set; } = "method";
private String versionName = "version";
/// <summary>
/// 版本号名称
/// </summary>
public String VersionName
{
get { return versionName; }
set { versionName = value; }
}
public string VersionName { get; set; } = "version";
private String charsetName = "charset";
/// <summary>
/// 编码名称
/// </summary>
/// <value>The name of the charset.</value>
public string CharsetName { get => charsetName; set => charsetName = value; }
public string CharsetName { get; set; } = "charset";
private String appKeyName = "app_id";
/// <summary>
/// appKey名称
/// </summary>
public String AppKeyName
{
get { return appKeyName; }
set { appKeyName = value; }
}
public string AppKeyName { get; set; } = "app_id";
private String dataName = "biz_content";
/// <summary>
/// data名称
/// </summary>
public String DataName
{
get { return dataName; }
set { dataName = value; }
}
public string DataName { get; set; } = "biz_content";
private String timestampName = "timestamp";
/// <summary>
/// 时间戳名称
/// </summary>
public String TimestampName
{
get { return timestampName; }
set { timestampName = value; }
}
public string TimestampName { get; set; } = "timestamp";
private String timestampPattern = "yyyy-MM-dd HH:mm:ss";
/// <summary>
/// 时间戳格式
/// </summary>
public String TimestampPattern
{
get { return timestampPattern; }
set { timestampPattern = value; }
}
private String signName = "sign";
/// <summary>
/// 签名串名称
/// </summary>
public String SignName
{
get { return signName; }
set { signName = value; }
}
public string SignName { get; set; } = "sign";
private String formatName = "format";
/// <summary>
/// 格式化名称
/// </summary>
public String FormatName
{
get { return formatName; }
set { formatName = value; }
}
private String formatType = "json";
/// <summary>
/// 格式类型
/// </summary>
public String FormatType
{
get { return formatType; }
set { formatType = value; }
}
private String accessTokenName = "app_auth_token";
/// <summary> accessToken名称
/// </summary>
public String AccessTokenName
{
get { return accessTokenName; }
set { accessTokenName = value; }
}
private String locale = "zh-CN";
/// <summary>
/// 国际化语言
/// </summary>
public String Locale
{
get { return locale; }
set { locale = value; }
}
private String responseCodeName = "code";
/// <summary>
/// 响应code名称
/// </summary>
public String ResponseCodeName
{
get { return responseCodeName; }
set { responseCodeName = value; }
}
private int connectTimeoutSeconds = 10;
/// <summary>
/// 请求超时时间
/// </summary>
public int ConnectTimeoutSeconds
{
get { return connectTimeoutSeconds; }
set { connectTimeoutSeconds = value; }
}
private int readTimeoutSeconds = 10;
/// <summary>
/// http读取超时时间
/// </summary>
public int ReadTimeoutSeconds
{
get { return readTimeoutSeconds; }
set { readTimeoutSeconds = value; }
}
private string signTypeName = "sign_type";
/// <summary>
/// 签名类型名称
/// </summary>
/// <value>The name of the sign type.</value>
public string SignTypeName { get => signTypeName; set => signTypeName = value; }
public string SignTypeName { get; set; } = "sign_type";
/// <summary>
/// 格式化名称
/// </summary>
public string FormatName { get; set; } = "format";
/// <summary> accessToken名称
/// </summary>
public string AccessTokenName { get; set; } = "app_auth_token";
/// <summary>
/// 国际化语言
/// </summary>
public string Locale { get; set; } = "zh-CN";
/// <summary>
/// 响应code名称
/// </summary>
public string ResponseCodeName { get; set; } = "code";
/// <summary>
/// 错误响应节点
/// </summary>
/// <value>The name of the error response.</value>
public string ErrorResponseName { get; set; } = "error_response";
/// <summary>
/// 请求超时时间
/// </summary>
public int ConnectTimeoutSeconds { get; set; } = 10;
/// <summary>
/// http读取超时时间
/// </summary>
public int ReadTimeoutSeconds { get; set; } = 10;
private DataNameBuilder dataNameBuilder = SdkConfig.dataNameBuilder;
/// <summary>
/// 节点名称构造器
/// </summary>
/// <value>The data name builder.</value>
public DataNameBuilder DataNameBuilder { get => dataNameBuilder; set => dataNameBuilder = value; }
public DataNameBuilder DataNameBuilder { get; set; } = DATA_NAME_BUILDER;
}
}

View File

@@ -0,0 +1,8 @@
using System;
namespace SDKCSharp.Common
{
public static class SopSdkConstants
{
public const string DATA_SUFFIX = "_response";
}
}

View File

@@ -0,0 +1,27 @@
using System;
namespace SDKCSharp.Common
{
public class SopSdkErrors
{
/// <summary>
/// 网络错误
/// </summary>
public static ErrorResponse HTTP_ERROR = BuildErrorResponse("836875001", "网络错误");
/// <summary>
/// 验证返回sign错误
/// </summary>
public static ErrorResponse CHECK_RESPONSE_SIGN_ERROR = BuildErrorResponse("836875002", "验证服务端sign出错");
public static ErrorResponse BuildErrorResponse(string code, string msg)
{
return new ErrorResponse
{
Code = code,
SubCode = code,
SubMsg = msg,
Msg = msg
};
}
}
}

View File

@@ -15,15 +15,18 @@ namespace SDKTest
{
static string url = "http://localhost:8081/api";
static string appId = "201904035630907729292csharp";
// 私钥, PKCS1 2048
// 开发者私钥, PKCS1 2048
static string privateKey = "MIIEowIBAAKCAQEA5+OvJxeSzf44NxQ/cl7Ii+BzPg2k6sRcvH4ffOtU5Dzq1/oEvg02nxIhmwOHBZmjbmuUu0aLsfglUTAwqfXftfAKZidshsgj9NNh0/kxk0avRZ1UoljWGz/FxVZA0ogbxxhohPZ9jWcD+eBQcIwF2DtHfAJqWWZrYFnCMeHD8mPzxo2kwXSvDzi0vf9I2tKiYvNG26a9FqeYtPOoi81sdS3+70HOMdxP8ejXtyfnKpKz7Dx506LCIRS5moWS3Q5eTLV3NGX/1CSJ8wpQA2DAQTjVhX5eVu7Yqz12t8W+sjWM/tHUR6cgwYYR10p7tSCeCPzkigjGxKm4cYXWtATQJQIDAQABAoIBAHFDsgrrJca+NKEan77ycwx3jnKx4WrWjOF4zVKL9AQjiSYDNgvKknJyPb3kpC/lEoHdxGERHSzJoxib7DkoIqRQYhPxj73pxj5QfYk3P7LLJNNg/LTrpXDb3nL8JV9wIflGf87qQvstZTDJEyFWE4jBs7Hr0BxovWvri8InnzkmERJ1cbGJgNHe1Y3Zo2tw0yaHxQCxLuajP+notRZhD9bEp7uKeI0w9AvlW6k8m/7y10F0BK/TlyW8rQiEC391yOiRYoMcUh4hd2Q9bMx3jngZgX8PXIvZZcup4/pvWlv1alwhB2tsnLdazP62r1MO80vLyLunzGO+7WwCjEYlVaECgYEA+lQRFmbhKaPuAuXMtY31Fbga8nedka5TjnEV7+/kX+yowE2OlNujF+ZG8UTddTxAGv56yVNi/mjRlgD74j8z0eOsgvOq9mwbCrgLhLo51H9O/wAxtb+hBKtC5l50pBr4gER6d8W6EQNTSGojnMIaLXTkAZ5Qf6Z8e2HFVdOn0X0CgYEA7SSrTokwzukt5KldNu5ukyyd+C3D1i6orbg6qD73EP9CfNMfGSBn7dDv9wMSJH01+Ty+RgTROgtjGRDbMJWnfbdt/61NePr9ar5sb6Nbsf7/I0w7cZF5dsaFYgzaOfQYquzXPbLQHkpMT64bqpv/Mwy4F2lFvaYWY5fA4pC2uckCgYEAg75Ym9ybJaoTqky8ttQ2Jy8UZ4VSVQhVC0My02sCWwWXLlXi8y7An+Rec73Ve0yxREOn5WrQT6pkmzh7V/ABWrYi5WxODpCIjtSbo0fLBa3Wqle00b0/hdCITetqIa/cFs1zUrOqICgK3bKWeXqiAkhhcwSZwwSgwOKM04Wn7ZUCgYBvhHX2mbdVJfyJ8kc+hMOE/E9RHRxiBVEXWHJlGi8PVCqNDq8qHr4g7Mdbzprig+s0yKblwHAvrpkseWvKHiZEjVTyDipHgShY4TGXEigVvUd37uppTrLi8xpYcJjS9gH/px7VCdiq1d+q/MJP6coJ1KphgATm2UrgDMYNBWaYWQKBgEHRxrmER7btUF60/YgcqPHFc8RpYQB2ZZE0kyKGDqk2Data1XYUY6vsPAU28yRLAaWr/D2H17iyLkxP80VLm6QhifxCadv90Q/Wl1DFfOJQMW6avyQ0so6G0wFq/LJxaFK4iLXQn1RJnmTp6BYiJMmK2BhFbRzw8ssMoF6ad2rr";
// 开放平台给的公钥
// 前往SOP-ADMINISV管理--秘钥管理选择PKCS1生成平台提供的公私钥然后把【平台公钥】放到这里
static string publicKeyPlatform = "";
// 从文件中加载
//static string filePath = "/Users/thc/logs/priKey.txt";
//static OpenClient client = new OpenClient(url, appId, filePath, true);
// 声明一个就行
static OpenClient client = new OpenClient(url, appId, privateKey);
static OpenClient client = new OpenClient(url, appId, privateKey, publicKeyPlatform);
public static void Main(string[] args)

View File

@@ -12,11 +12,7 @@ namespace SDKCSharp.Request
/// <typeparam name="T">对应的Response对象</typeparam>
public abstract class BaseRequest<T>
{
private string method;
private string format = SdkConfig.FORMAT_TYPE;
private Encoding charset = SdkConfig.CHARSET;
private SignType signType = SdkConfig.SIGN_TYPE;
private string timestamp = DateTime.Now.ToString(SdkConfig.TIMESTAMP_PATTERN);
private string method;
private string version;
private string bizContent;
@@ -30,8 +26,6 @@ namespace SDKCSharp.Request
public string BizContent { set => bizContent = value; }
public object BizModel { set => bizModel = value; }
public string Version { get => version; set => version = value; }
public Encoding Charset { get => charset; set => charset = value; }
public SignType SignType { get => signType; set => signType = value; }
/// <summary>
/// 返回接口名
@@ -45,7 +39,7 @@ namespace SDKCSharp.Request
/// <returns></returns>
public virtual string GetVersion()
{
return SdkConfig.DEFAULT_VERSION;
return null;
}
/// <summary>
@@ -66,7 +60,7 @@ namespace SDKCSharp.Request
protected BaseRequest(string name, string version)
{
this.method = name;
this.version = version == null ? SdkConfig.DEFAULT_VERSION : version;
this.version = version;
}
/// <summary>
@@ -88,23 +82,28 @@ namespace SDKCSharp.Request
/// <returns></returns>
public RequestForm CreateRequestForm(OpenConfig openConfig)
{
Dictionary<string, string> dict = new Dictionary<string, string>();
dict[openConfig.MethodName] = this.Method;
dict[openConfig.FormatName] = this.format;
dict[openConfig.CharsetName] = this.charset.BodyName;
dict[openConfig.SignTypeName] = this.signType.ToString();
dict[openConfig.TimestampName] = this.timestamp;
dict[openConfig.VersionName] = this.version;
string timestamp = DateTime.Now.ToString(openConfig.TimestampPattern);
string v = this.version ?? openConfig.DefaultVersion;
// 业务参数
String biz_content = BuildBizContent();
string biz_content = BuildBizContent();
dict[openConfig.DataName] = biz_content;
Dictionary<string, string> dict = new Dictionary<string, string>
{
[openConfig.MethodName] = this.Method,
[openConfig.FormatName] = openConfig.FormatType,
[openConfig.CharsetName] = openConfig.Charset.BodyName,
[openConfig.SignTypeName] = openConfig.SignType.ToString(),
[openConfig.TimestampName] = timestamp,
[openConfig.VersionName] = v,
[openConfig.DataName] = biz_content
};
RequestForm requestForm = new RequestForm(dict);
requestForm.Charset = this.charset;
requestForm.RequestMethod = GetRequestMethod();
requestForm.Files = this.files;
RequestForm requestForm = new RequestForm(dict)
{
Charset = openConfig.Charset,
RequestMethod = GetRequestMethod(),
Files = this.files
};
return requestForm;
}

View File

@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using SDKCSharp.Request;
namespace SDKCSharp.Utility
{
@@ -36,6 +37,7 @@ namespace SDKCSharp.Utility
return JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
}
/// <summary>
/// 对象转换成json字符串
/// </summary>

View File

@@ -2,13 +2,14 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace SDKCSharp.Utility
{
/// <summary>
/// 签名工具类
/// </summary>
public class SignUtil
public static class SignUtil
{
/// <summary>
@@ -26,6 +27,13 @@ namespace SDKCSharp.Utility
return rsa.Sign(content);
}
public static bool RsaCheck(string content, string sign, string publicKeyPlatform, Encoding charset,
SignType signType)
{
RSAHelper rsa = new RSAHelper(signType, charset, null, publicKeyPlatform);
return rsa.Verify(content, sign);
}
/// <summary>
/// 构建签名内容
/// </summary>
@@ -53,5 +61,33 @@ namespace SDKCSharp.Utility
return content;
}
/// <summary>
/// 构建签名内容
/// </summary>
/// <returns>The sign content.</returns>
/// <param name="parameters">Parameters.</param>
public static string GetSignContentObject(IDictionary<string, object> parameters)
{
// 第一步把字典按Key的字母顺序排序
IDictionary<string, object> sortedParams = new SortedDictionary<string, object>(parameters);
IEnumerator<KeyValuePair<string, object>> dem = sortedParams.GetEnumerator();
// 第二步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder("");
while (dem.MoveNext())
{
string key = dem.Current.Key;
string value = Convert.ToString(dem.Current.Value);
value = Regex.Replace(value, @"\s", "");
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
{
query.Append(key).Append("=").Append(value).Append("&");
}
}
string content = query.ToString().Substring(0, query.Length - 1);
return content;
}
}
}