这两天 一直和京东对接接口,我们用.net api 提供接口,对方用java调用,本来没什么问题,但是对方对数据安全要求特别严,要验签,于是噩梦开始了。

1、在传输的时候,约定传输格式:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);//+ "?RequestData="+ paramrequest.Method = "POST";request.ContentType = "application/json";//参数是接送//request.ContentType = "application/x-www-form-urlencoded";//参数为&拼接request.ContentLength = param.Length;

2、双方平台对编码不一致,所以在对数据进行MD5加密前,先进行UTF8编码:

byte[] 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在传输的过程中,+、/、=会丢失,所以使用了替换

string base64Token2 = base64Token.Replace('+', '-').Replace('/', '_').Replace('=', '*');

5、我们使用模型接收数据,这时候,会出现数据接收不到的情况,那么上面1的ContentType 就显得比较重要了,既然我上面已经注释了,就不多写了。

6、应为我们使用模型接收数据,而对方见有的数据不是必填的,所以就没有写,这样对方填3个参数,进行加密计算,而我这边会把没有填的null值也加进来进行加密计算,并且计算的时候,我们这边采用序列化,使用两边还要对参数进行排序,所以,我们的加密结果始终不一样,崩溃┭┮﹏┭┮,中间考虑过用string接收参数,但是api不支持直接接收string参数,必须要加一个[FromBody]的标记,然而,问题有开始来了,对方的请求,根本进不来,继续崩溃┭┮﹏┭┮。最终我们还是使用模型对数据进行接收,不过接收参数的方式改了一下,用流来接收,这样,对方传什么,我们接收的就是什么,具体代码如下:

这是原来的代码

            var jsonSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };var dataOld = JsonConvert.SerializeObject(actionContext.ActionArguments[ArgumentsName], Formatting.Indented, jsonSetting);//这两行用来去除为空的参数
dataOld = JsonSort.SortJson(JToken.Parse(dataOld), null);//排序

这是修改后的代码

Stream stream = HttpContext.Current.Request.InputStream;StreamReader streamReader = new StreamReader(stream);responseJson = streamReader.ReadToEnd();

可以看到 接收参数的方式由 actionContext.ActionArguments[ArgumentsName] 换成了下面的 streamReader.ReadToEnd();如果有哪位大神指导string类型的怎么发送,怎么接收,还请告知一下,毕竟第一次做,没有经验

7、整体的验签处理使用的是ActionFilterAttribute 拦截,具体的思路就是,将秘钥各自保存一份,将数据用秘钥加密,然后将验签的Token可用户标识(不是秘钥)写一份到cookie里面,然后逻辑上就可以不做任何更改直接使用啦

具体的代码如下,但是验签部分因为对方是京东,还是省略的好,大家可以根据自己的应用场景脑补

using 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中取出Tokenif (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;//获取到Tokentag = cookieToken[UserTagName].Value;//获取到Tagint 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平台对接技术总结相关推荐

  1. 极光推送REST API与Java后台对接

    极光推送官网的web推送页面 因为是对接它的api,所以我参照这这个样式实现了一个,效果如下: 定时任务推送界面,可定制.实现了推送一次和每日定时推送,如果再扩展的话有每周.每月的功能,只是没有这个业 ...

  2. 获取淘宝商品历史价格信息API(PHP,JAVA都可对接)

    如何知道淘宝商品的历史价格信息,其实可以通过api接口接入信息的: Result Object: { "items": { "num_iid": "6 ...

  3. java平台脚本+java编译器API

    [0]README 0.1)本文文字描述转自 core java volume 2, 旨在学习  java平台脚本+java编译器API 的 基础知识: ----------------------- ...

  4. Java平台,标准版Oracle JDK 9中的新功能

    Java平台,标准版 Oracle JDK 9中的新增功能 版本9 E77563-05 2017年9月 JDK 9中的新功能概述 Java Platform,Standard Edition 9是一个 ...

  5. 海康 综合安防管理平台 对接

    海康摄像头对接,通过海康综合安防管理平台对接 1. 海康综合安防管理平台介绍 1.1 官网介绍 1.2 个人理解 综合安防管理平台部署之后,有2个系统,一个是综合安防管理平台:是用户端系统,一个是运营 ...

  6. 电商平台对接第三方快递鸟物流轨迹查询api接口申请对接全流程

    电商平台对接第三方快递物流轨迹查询api接口申请对接全流程 快递鸟查询API接口是使用的物流单号即可实现查询物流信息.主要应用在电商商城.ERP系统商.WMS系统商.快递柜.银行等企业.多家快递物流公 ...

  7. 淘宝天猫开放平台店铺商品发布(新)-淘宝店铺发布API接口流程代码对接说明

    淘宝天猫开放平台店铺商品发布(新)-淘宝店铺发布API接口,天猫店铺发布API接口,oAuth2.0店铺发布接口,店铺商品API接口,店铺商品接口发布API接口流程代码对接说明: 公共参数 名称 类型 ...

  8. 使用Java蓝牙无线通讯技术API

    蓝牙是一种低成本.短距离的无线通信技术.对于那些希望创建个人局域网(PANs)的人们来说,蓝牙技术已经越来越流行了.每个个人局域网都在独立设备的周围被动态地创建,并且为蜂窝式电话和PDA等设备提供了自 ...

  9. 使用Java蓝牙无线通讯技术API概述

    蓝牙是一种低成本.短距离的无线通信技术.对于那些希望创建个人局域网(pans)的人们来说,蓝牙技术已经越来越流行了.每个个人局域网都在独立设备的周围被动态地创建,并且为蜂窝式电话和pda等设备提供了自 ...

最新文章

  1. 自己动手写一个印钞机 第四章
  2. RIM发警告 部分黑莓手机存在安全漏洞
  3. 字符串-字符串的查找和替换
  4. python列表反向取值_Python列表的反向遍历,python,逆序
  5. 快速打开计算机磁盘的软件,怎样快速启动电脑
  6. vue2.0实现点击后显示,再次点击隐藏
  7. 小学生 计算机编程 教程,小学生C++创意编程(视频教学版)
  8. 求解函数优化问题的改进布谷鸟搜索算法
  9. 基于Linux系统的网络聊天室实现
  10. 迅雷手机版苹果版_IOS手机迅雷下载(支持苹果手机和ipad)
  11. 在线支付接口详解、支付接口对接
  12. android 4.4优化build.prop,Androidbuild.prop详细优化
  13. 计算机磁盘检查,使用磁盘检查工具进行硬盘诊断
  14. 明日之后最新服务器开服时间,明日之后什么时候开服 新服开区详解
  15. B450M MORTAR    AMD R5 3600   组装机
  16. 棋牌类游戏测试用例怎么写?我敢打赌你绝对不知道
  17. C++ 高效编程:pass-by-value(值传递)与pass-by-reference(引用传递)
  18. 网上期货开户合约签署流程
  19. 项目经理必须具备的十大管理技能
  20. 竞价账户时好时坏怎样分析找到原因?

热门文章

  1. java判断是否包含张三_c# 数组 字符串 C#中判断字符串中包含某个字符
  2. python 列表自定义排序_自定义排序的Python列表
  3. 计算机nit题百度云,计算机NIT应用基础试题
  4. IllegalThreadStateException
  5. GitHub:一份玩转 GitHub 的秘诀,值得收藏!
  6. 搞清这些陷阱,NULL和三值逻辑再也不会作妖
  7. 在程序员面前千万不要说这9句话,我一个同事就死的很惨!
  8. 两张趣图助你理解状态码的含义~
  9. mysql开方_MySQL数学函数的实际用法
  10. mysql kill_Mysql使用kill命令解决死锁问题(杀死某条正在执行的sql语句)