.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.pswp.cn/news/280135.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280135.shtml
英文地址,請注明出處:http://en.pswp.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…