.net api 和java平台对接技术总结

这两天 一直和京东对接接口,我们用.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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/280135.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Burpsuite学习(4)

2019独角兽企业重金招聘Python工程师标准>>> burpsuite spider模块通过跟踪 HTML 和 JavaScript 以及提交的表单中的超链接来映射目标应用程序&#xff0c;它还使用了一些其他的线索&#xff0c;如目录列表&#xff0c;资源类型的注释&#xff0c;以及 robots.txt 文…

Git删除分支/恢复分支

这是https://www.cnblogs.com/utank/p/7880441.html的方法&#xff0c;虽然很老现在有点不一样&#xff0c;但总体还是能用的。 总结就是两种方法 1.用commit的id恢复 2.用reflog的头指针恢复 •删除一个已被终止的分支 如果需要删除的分支不是当前正在打开的分支&#xff0c;使…

NetCore2.0Web应用之Startup

为什么80%的码农都做不了架构师&#xff1f;>>> 作为main函数的程序启动文件UseStartup 默认就是调用我们的整个应用程序的启动文件 class Program{static void Main(string[] args){var host new WebHostBuilder().UseKestrel() // 指定WebServer为Kes…

windows XP系统下oracle完整卸载过程

NT环境下&#xff1a; 1、以NT的Administrator 登陆 2、通过控制面版-〉服务&#xff0c;停掉所有ORACLE服务 3、打开注册表&#xff08;REGEDIT命令&#xff09;&#xff0c;删除HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE 4、打开注册表中的 HKEY_LOCAL_MACHINE\SYSTEM\CurrentCont…

Hadoop----hdfs的基本操作

2019独角兽企业重金招聘Python工程师标准>>> HDFS操作文件的基本命令 1.创建文件夹 $>hdfs dfs -mkdir /user/centos/hadoop 2.展示目录 $>hdfs dfs -ls -r /user/centos/hadoop 3.递归展示 $>hdfs dfs -lsr /user/centos/hadoop 4.上传文件 $&g…

原生sql实现restful接口调用

index.php <?phpinclude ./Request.php; include ./Response.php; //获取数据 $dataRequest::getRequest(); $resultResponse::sendResponse($data); echo $result; ?> Request.php <?php class Request{private static $method_typearray(get,post,put,delete,pa…

弹幕效果

<!DOCTYPE html><html> <head> <meta charset"UTF-8"> <title>弹幕效果</title> <script src"../../jquery-1.12.4.min.js" type"text/javascript" charset"utf-8"></script> </h…

基于.NetCore开发博客项目 StarBlog - (21) 开始开发RESTFul接口

1前言最近电脑坏了&#xff0c;开源项目的进度也受到一些影响这篇酝酿很久了&#xff0c;作为本系列第二部分&#xff08;API接口开发&#xff09;的第一篇&#xff0c;得想一个好的开头&#xff0c;想着想着就鸽了好久&#xff0c;索性不扯那么多了&#xff0c;直接开写吧~2关…

03 Oracle分区表

Oracle分区表 先说句题外话… 欢迎成都天府软件园的小伙伴来面基交流经验~ 一&#xff1a;什么是分区&#xff08;Partition&#xff09;&#xff1f; 分区是将一个表或索引物理地分解为多个更小、更可管理的部分。 分区对应用透明&#xff0c;即对访问数据库的应用而言&…

windows获取本地时间_如何在Windows 8中重新获得本地登录

windows获取本地时间By default a fresh Windows 8 installation prompts you to create a synchronized cloud-enabled login. While there are distinct perks to Microsoft’s live login system, sometimes you just want to keep things simple and local. Read on as we …

如何解决高并发,秒杀问题

相信不少人会被这个问题困扰&#xff0c;分享大家一篇这样的文章&#xff0c;希望能够帮到你&#xff01; 一、秒杀业务为什么难做&#xff1f;1&#xff09;im系统&#xff0c;例如qq或者微博&#xff0c;每个人都读自己的数据&#xff08;好友列表、群列表、个人信息&#xf…

Spring原理之代理与动态代理模式总结(四)

2019独角兽企业重金招聘Python工程师标准>>> 代理模式 1&#xff0c;什么是代理模式&#xff1f; 代理模式的作用是&#xff1a;为其他对象提供一种代理以控制对这个对象的访问。2&#xff0c;代理模式有什么好处&#xff1f; 在某些情况下&#xff0c;一个客户不…

可执行文件添加快捷方式_如何停止Windows向快捷方式文件名添加“-快捷方式”...

可执行文件添加快捷方式When you make a new shortcut in Windows, it automatically adds “- Shortcut” to the end of the shortcut’s file name. This doesn’t seem like a big deal, but they can be bothersome. Sure, you can remove the text yourself when you cre…

Red hat6.4重新安装yum

今天在Red Hat上安装软件时&#xff0c;发现需要依赖软件&#xff0c;然而在用yum指令时&#xff0c;出现了下面的错误&#xff1a; This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register. 出现这个问题的原因是&…

使用 BenchmarkDotNet 比较指定容量的 List 的性能

我们之前提到 List 是 .NET 中常用的数据结构&#xff0c;其在存储大量数据时&#xff0c;如果能够指定它的初始化容量&#xff0c;就会有性能提升。这个优化的方法并不是很明显&#xff0c;因此本文将使用 BenchmarkDotNet 库&#xff0c;通过定量对比的方式来证明这一点。实验…

看明星合影争C位,学PPT中C位排版法

在娱乐圈里&#xff0c;C位是大咖位&#xff0c;是对艺人实力的最好证明&#xff0c;艺人们自然会想着去力争C位&#xff0c;正所谓“不想当将军的兵不是好兵&#xff0c;不想站C位的明星不是好明星”。那么&#xff0c;C位是什么意思&#xff1f;C位&#xff0c;网络流行语&am…

javafx由浅到深的 认识(一)

javafx是一款比较新兴的语言框架,随着javafx越来越实用,估计许多程序员也会慢慢接触它,故我在这里对它由浅到深进行介绍一下. 首先,要了解javafx,就应该先知道.xml文件的布局软件,以往java都是通过敲代码来进行布局的,但javafx有力新的突破,它实现了拖动方式,目前我使用的辅助软…

linux用户的根目录_为什么Linux允许用户删除根目录?

linux用户的根目录Most of the time, none of us willingly performs an action that will literally break our operating systems and force us to reinstall them. But what if such an action could easily occur even by accident on the user’s part? Today’s SuperUs…

纯css实现叉号

HMTL部分 <a href"#" class"close"></a> CSS部分 .close {position: absolute;right: 32px;top: 32px;width: 32px;height: 32px; } .close:before, .close:after {position: absolute;left: 15px;content: ;height: 33px;width: 2px;backgro…

微软跨平台maui开发chatgpt客户端

image什么是maui.NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创建本机移动(ios,andriod)和桌面(windows,mac)应用。imagechagpt最近这玩意很火&#xff0c;由于网页版本限制了ip&#xff0c;还得必须开代理&#xff0c; 用起来比较麻烦&a…