这两天 一直和京东对接接口,我们用.net api 提供接口,对方用java调用,本来没什么问题,但是对方对数据安全要求特别严,要验签,于是噩梦开始了。
1、在传输的时候,约定传输格式:
1
2
3
4
5HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);//+ "?RequestData="+ param request.Method = "POST"; request.ContentType = "application/json";//参数是接送 //request.ContentType = "application/x-www-form-urlencoded";//参数为&拼接 request.ContentLength = param.Length;
2、双方平台对编码不一致,所以在对数据进行MD5加密前,先进行UTF8编码:
1
2byte[] md5Token = md5.ComputeHash(Encoding.UTF8.GetBytes(data)); string base64Token = Convert.ToBase64String(md5Token);
3、我们遇到了这个问题:https://bbs.csdn.net/topics/340058520
具体的描述就是C#和java对二进制的编码值不一样
java byte : -128~127
C# byte : 0~255
但是,这只是视觉欺骗,做硬件的经理说,虽然两边看到对字符的编码得到的值不一样,但是,实际上,计算机对这个的值的识别是一样的。所以这根本就不是个问题。但是对方特别有毅力,反复的试了各种编码格式,于是我就跟在他后面,一个一个的试,又是远程合作,不得不佩服,研究生果然比我这种本科毕业的做事有毅力,然而,最后发现,是他在做加密的时候,忘记把私钥放进去了。哎,感觉身体被掏空┭┮﹏┭┮
4、Cookie在传输的过程中,+、/、=会丢失,所以使用了替换
1string base64Token2 = base64Token.Replace('+', '-').Replace('/', '_').Replace('=', '*');
5、我们使用模型接收数据,这时候,会出现数据接收不到的情况,那么上面1的ContentType 就显得比较重要了,既然我上面已经注释了,就不多写了。
6、应为我们使用模型接收数据,而对方见有的数据不是必填的,所以就没有写,这样对方填3个参数,进行加密计算,而我这边会把没有填的null值也加进来进行加密计算,并且计算的时候,我们这边采用序列化,使用两边还要对参数进行排序,所以,我们的加密结果始终不一样,崩溃┭┮﹏┭┮,中间考虑过用string接收参数,但是api不支持直接接收string参数,必须要加一个[FromBody]的标记,然而,问题有开始来了,对方的请求,根本进不来,继续崩溃┭┮﹏┭┮。最终我们还是使用模型对数据进行接收,不过接收参数的方式改了一下,用流来接收,这样,对方传什么,我们接收的就是什么,具体代码如下:
这是原来的代码
1
2
3
4var jsonSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; var dataOld = JsonConvert.SerializeObject(actionContext.ActionArguments[ArgumentsName], Formatting.Indented, jsonSetting);//这两行用来去除为空的参数 dataOld = JsonSort.SortJson(JToken.Parse(dataOld), null);//排序
这是修改后的代码
1
2
3Stream stream = HttpContext.Current.Request.InputStream; StreamReader streamReader = new StreamReader(stream); responseJson = streamReader.ReadToEnd();
可以看到 接收参数的方式由 actionContext.ActionArguments[ArgumentsName] 换成了下面的 streamReader.ReadToEnd();如果有哪位大神指导string类型的怎么发送,怎么接收,还请告知一下,毕竟第一次做,没有经验
7、整体的验签处理使用的是ActionFilterAttribute 拦截,具体的思路就是,将秘钥各自保存一份,将数据用秘钥加密,然后将验签的Token可用户标识(不是秘钥)写一份到cookie里面,然后逻辑上就可以不做任何更改直接使用啦
具体的代码如下,但是验签部分因为对方是京东,还是省略的好,大家可以根据自己的应用场景脑补
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206using Aito.Entity; using Aito.ServBll.JDBll; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using System.Web.Script.Serialization; using ZP.Comm; namespace Aito.JDService { /// <summary> /// 在action执行前后做额外处理 /// </summary> public class TokenProjectorAttribute : ActionFilterAttribute { public string TokenName { get; set; } = "Token"; public string UserTagName { get; set; } = "UserTag"; public string ArgumentsName { get; set; } = "RequestData"; public string UserInfoName { get; set; } = "UserInfo"; /// <summary> /// 在action执行之前验证Token的合法性 /// </summary> /// <param name="actionContext"></param> //public override void OnActionExecuting(HttpActionContext actionContext) public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken) { string token = "",tag=""; CookieHeaderValue cookieToken = actionContext.Request.Headers.GetCookies(TokenName).FirstOrDefault();//从cookie中取出Token if (cookieToken == null) ThrowException(new JDServiceModel<string>() { Success = false, Code = -4001, Msg = "Token不能为空!", Data = "" }); CookieHeaderValue cookieTag = actionContext.Request.Headers.GetCookies(UserTagName).FirstOrDefault();//从cookie中取出用户标识 if (cookieTag == null) ThrowException(new JDServiceModel<string>() { Success = false, Code = -4002, Msg = "用户标识能为空!", Data = "" }); token = cookieToken[TokenName].Value;//获取到Token tag = cookieToken[UserTagName].Value;//获取到Tag int userID = -1; if(!int.TryParse(tag,out userID)) ThrowException(new JDServiceModel<string>() { Success = false, Code = -4007, Msg = "用户标识错误!", Data = "" }); //验证用户合法性 if (CommOpreJD.UserInfos.Where(u => u.UserID == userID).Count() < 1) ThrowException(new JDServiceModel<string>() { Success = false, Code = -4003, Msg = "用户标识不合法!", Data = "" }); //验证数据的合法性 if (!actionContext.ActionArguments.ContainsKey(ArgumentsName)) ThrowException(new JDServiceModel<string>() { Success = false, Code = -4004, Msg = "请求参数不能为空!", Data = "" }); string msg = "68行:请求Token:"+ token + "n"; msg+= "UserTag:" + tag + "n"; ErrHandler.WriteServerInfo(msg); //====================================================参数验证 var modelState = actionContext.ModelState; if (!modelState.IsValid) { string error = string.Empty; foreach (var key in modelState.Keys) { var state = modelState[key]; if (state.Errors.Any()) { error = state.Errors.First().ErrorMessage; if (String.IsNullOrEmpty(error)) { error = "请求参数缺失或错误"; } break; } } ThrowException(new JDServiceModel<string>() { Success = false, Code = -4008, Msg = "参数验证失败-" + error, Data = "" }); } //====================================================参数验证 //校验数据是否被篡改 UserInfoServModel model = CommOpreJD.UserInfos.Where(u => u.UserID == userID).ToList()[0]; #region 验签 string responseJson = string.Empty; try { Stream stream = HttpContext.Current.Request.InputStream; StreamReader streamReader = new StreamReader(stream); responseJson = streamReader.ReadToEnd(); //responseJson = JsonSort.SortJson(JToken.Parse(responseJson), null); } catch { } //var jsonSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; //var dataOld = JsonConvert.SerializeObject(actionContext.ActionArguments[ArgumentsName], Formatting.Indented, jsonSetting);//这两行用来去除为空的参数 //dataOld = JsonSort.SortJson(JToken.Parse(dataOld), null); 此处为验签操作,目的是计算出 base64Token2 msg = "108行:用户数据:" + data + "n"; msg += "用户Token:" + token + "n"; msg += "校验Token:" + base64Token2 + "n"; ErrHandler.WriteServerInfo(msg); if (base64Token2 != token) ThrowException(new JDServiceModel<string>() { Success = false, Code = -4005, Msg = "数据被篡改!", Data = "" }); #endregion actionContext.Request.Properties[UserInfoName] = model; return base.OnActionExecutingAsync(actionContext, cancellationToken); } private void ThrowException(JDServiceModel<string> exp) { var response = new HttpResponseMessage(); response.Content = new StringContent(new JavaScriptSerializer().Serialize(exp)); response.StatusCode = HttpStatusCode.Conflict; throw new HttpResponseException(response); } /// <summary> /// 在Action方法调用后,result方法调用前执行,使用场景:异常处理。 /// </summary> /// <param name="actionExecutedContext"></param> //public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) { string resultData = GetResponseValues(actionExecutedContext); object user = null; if (!actionExecutedContext.Request.Properties.TryGetValue(UserInfoName, out user)) ThrowException(new JDServiceModel<string>() { Success = false, Code = -4006, Msg = "数据返回时,用户信息丢失!", Data = "" }); //HttpStatusCode StatusCode = actionExecutedContext.ActionContext.Response.StatusCode;// 取得由 API 返回的状态代码 JDServiceModel<string> result = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<JDServiceModel<string>>().Result;// 取得由 API 返回的资料 result.Success = actionExecutedContext.ActionContext.Response.IsSuccessStatusCode; //请求是否成功 此处为验签操作,目的是计算出 base64Token2 result.Token = base64Token2; // 重新封装回传格式 actionExecutedContext.Response = ToJson(result); string msg = "返回数据:" + result.Data + "n"; msg += "返回Token:" + base64Token2 + "n"; ErrHandler.WriteServerInfo(msg); ErrHandler.WriteServerInfo(msg); return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken); //base.OnActionExecuted(actionExecutedContext); } /// <summary> /// 读取action返回的result /// </summary> /// <param name="actionExecutedContext"></param> /// <returns></returns> private string GetResponseValues(HttpActionExecutedContext actionExecutedContext) { Stream stream = actionExecutedContext.Response.Content.ReadAsStreamAsync().Result; Encoding encoding = Encoding.UTF8; /* 这个StreamReader不能关闭,也不能dispose, 关了就傻逼了 因为你关掉后,后面的管道 或拦截器就没办法读取了 */ var reader = new StreamReader(stream, encoding); string result = reader.ReadToEnd(); /* 这里也要注意: stream.Position = 0; 当你读取完之后必须把stream的位置设为开始 因为request和response读取完以后Position到最后一个位置,交给下一个方法处理的时候就会读不到内容了。 */ stream.Position = 0; return result; } private HttpResponseMessage ToJson(Object obj) { String str; if (obj is String || obj is Char)//如果是字符串或字符直接返回 { str = obj.ToString(); } else//否则序列为json字串 { JavaScriptSerializer serializer = new JavaScriptSerializer(); str = serializer.Serialize(obj); } HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str, Encoding.GetEncoding("UTF-8"), "application/json") }; return result; } } }
参考出处:
https://www.cnblogs.com/hnsongbiao/p/7039666.html
https://www.cnblogs.com/goodlucklzq/p/4481956.html
转载于:https://www.cnblogs.com/bamboo-zhang/p/9177087.html
最后
以上就是昏睡黑猫最近收集整理的关于.net api 和java平台对接技术总结的全部内容,更多相关.net内容请搜索靠谱客的其他文章。
发表评论 取消回复