本文以.NET語言為例,介紹如何在服務端完成簽名,並設定上傳回調,然後通過表單直傳資料到OSS。
前提條件
- 應用伺服器對應的網域名稱可通過公網訪問。
- 應用伺服器已安裝Visual Studio 2012或更高版本。
- 應用伺服器已經安裝.Net Framework 4.5及以上版本。
步驟1:配置應用伺服器
- 下載應用伺服器源碼(.NET版本)。
- 本樣本中以Windows環境為例,將下載的檔案解壓到C:\callback-server-dotnet目錄下。
- 進入該目錄,找到並開啟源碼檔案TinyHttpServer.cs,修改如下的程式碼片段:
// 請填寫您的AccessKeyId。 public static string accessKeyId = "<yourAccessKeyId>"; // 請填寫您的AccessKeySecret。 public static string accessKeySecret = "<yourAccessKeySecret>"; // host的格式為https://bucketname.endpoint,請替換為您的真實資訊。 public static string host = "https://bucketname.oss-cn-hangzhou.aliyuncs.com"; // callbackUrl為上傳回調伺服器的URL,請將下面的IP和Port配置為您自己的真實資訊。 public static string callbackUrl = "http://192.168.0.1:8888"; // 使用者上傳檔案時指定的首碼。 public static string uploadDir = "user-dir-prefix/";
- accessKeyId:設定您的AccessKeyId。
- accessKeySecret:設定您的AessKeySecret。
- host:格式為https://bucketname.endpoint,例如https://bucket-name.oss-cn-hangzhou.aliyuncs.com。關於Endpoint的介紹,請參見Endpoint訪問網域名稱。
- callbackUrl:設定上傳回調URL,即回調伺服器位址,用於處理應用伺服器與OSS之間的通訊。OSS會在檔案上傳完成後,把檔案上傳資訊通過此回調URL發送給應用伺服器。本例中修改為:
String callbackUrl ="http://10.10.10.10:1234";
。 - uploadDir:設定上傳到OSS檔案的首碼,以便於區分檔案。您也可以填寫空值。
- 使用Visual Studio開啟aliyun-oss-net-callback-server.sln工程檔案,單擊啟動,產生執行程式aliyun-oss-net-callback-server.exe。
步驟2:配置用戶端
- 下載用戶端源碼。
- 將檔案解壓,本例中以解壓到
D:\aliyun\aliyun-oss-appserver-js
目錄為例。 - 進入該目錄,開啟
upload.js
檔案,找到下面的代碼語句:// serverUrl是使用者擷取簽名和Policy等資訊的應用伺服器的URL,請將下面的IP和Port配置為您自己的真實資訊。 serverUrl = 'http://192.168.0.1:8888'
- 將
severUrl
改成應用伺服器的地址,用戶端可以通過它擷取簽名直傳Policy等資訊。例如本樣本中可修改為:serverUrl = 'http://10.10.10.10:1234'
。
步驟3:修改CORS
用戶端進行表單直傳到OSS時,會從瀏覽器向OSS發送帶有Origin
的請求訊息。OSS對帶有Origin
頭的請求訊息會進行跨域規則(CORS)的驗證。因此需要為Bucket設定跨域規則以支援Post方法。
步驟4:體驗上傳回調
- 啟動應用伺服器。在應用伺服器的命令列視窗下,進入到aliyun-oss-net-callback-server.exe執行程式產生的目錄下,執行命令aliyun-oss-net-callback-server.exe ${ip} ${port}啟動應用伺服器。
cd C:\Users\Desktop\callback-server-dotnet\aliyun-oss-net-callback-server\bin\Debug\ aliyun-oss-net-callback-server.exe 10.10.10.10 1234
說明- 程式目錄以實際環境為準。您在使用Visual Studio產生aliyun-oss-net-callback-server.exe程式時,可以看到程式目錄。
- ${ip}和${port} 修改成配置應用伺服器的IP和連接埠。例如本樣本中為aliyun-oss-net-callback-server.exe 10.10.10.10 1234。
- 啟動用戶端。
- 在PC端的用戶端源碼目錄中,開啟index.html檔案。重要
index.html檔案不保證相容IE 10以下版本瀏覽器,若使用IE 10以下版本瀏覽器出現問題時,您需要自行調試。
- 單擊選擇檔案,選擇指定類型的檔案,單擊開始上傳。上傳成功後,顯示回調伺服器返回的內容。
- 在PC端的用戶端源碼目錄中,開啟index.html檔案。
應用伺服器核心代碼解析
應用伺服器源碼包含了簽名直傳服務和上傳回調服務兩個功能。
- 簽名直傳服務響應用戶端發送給應用伺服器的GET訊息,程式碼片段如下:
private static string GetPolicyToken() { //expireTime var expireDateTime = DateTime.Now.AddSeconds(expireTime); // example of policy //{ // "expiration": "2020-05-01T12:00:00.000Z", // "conditions": [ // ["content-length-range", 0, 1048576000] // ["starts-with", "$key", "user-dir-prefix/"] // ] //} //policy var config = new PolicyConfig(); config.expiration = FormatIso8601Date(expireDateTime); config.conditions = new List<List<Object>>(); config.conditions.Add(new List<Object>()); config.conditions[0].Add("content-length-range"); config.conditions[0].Add(0); config.conditions[0].Add(1048576000); config.conditions.Add(new List<Object>()); config.conditions[1].Add("starts-with"); config.conditions[1].Add("$key"); config.conditions[1].Add(uploadDir); var policy = JsonConvert.SerializeObject(config); var policy_base64 = EncodeBase64("utf-8", policy); var signature = ComputeSignature(accessKeySecret, policy_base64); //callback var callback = new CallbackParam(); callback.callbackUrl = callbackUrl; callback.callbackBody = "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}"; callback.callbackBodyType = "application/x-www-form-urlencoded"; var callback_string = JsonConvert.SerializeObject(callback); var callback_string_base64 = EncodeBase64("utf-8", callback_string); var policyToken = new PolicyToken(); policyToken.accessid = accessKeyId; policyToken.host = host; policyToken.policy = policy_base64; policyToken.signature = signature; policyToken.expire = ToUnixTime(expireDateTime); policyToken.callback = callback_string_base64; policyToken.dir = uploadDir; return JsonConvert.SerializeObject(policyToken); } public void DoGet() { Console.WriteLine("DoGet request: {0}", this.httpURL); var content = GetPolicyToken(); this.swResponse.WriteLine("HTTP/1.0 200 OK"); this.swResponse.WriteLine("Content-Type: application/json"); this.swResponse.WriteLine("Access-Control-Allow-Origin: *"); this.swResponse.WriteLine("Access-Control-Allow-Method: GET, POST"); this.swResponse.WriteLine($"Content-Length: {content.Length.ToString()}"); this.swResponse.WriteLine("Connection: close"); this.swResponse.WriteLine(""); this.swResponse.WriteLine(content); }
- 上傳回調服務響應OSS發送給應用伺服器的POST訊息,程式碼片段如下:
public bool VerifySignature() { // Get the Authorization Base64 from Request if (this.httpHeadersDict["authorization"] != null) { this.strAuthorizationRequestBase64 = this.httpHeadersDict["authorization"].ToString(); } else if (this.httpHeadersDict["Authorization"] != null) { this.strAuthorizationRequestBase64 = this.httpHeadersDict["Authorization"].ToString(); } if (this.strAuthorizationRequestBase64 == "") { Console.WriteLine("authorization property in the http request header is null. "); return false; } // Decode the Authorization from Request this.byteAuthorizationRequest = Convert.FromBase64String(this.strAuthorizationRequestBase64); // Decode the URL of PublicKey this.strPublicKeyURLBase64 = this.httpHeadersDict["x-oss-pub-key-url"].ToString(); var bytePublicKeyURL = Convert.FromBase64String(this.strPublicKeyURLBase64); var strAsciiPublickeyURL = System.Text.Encoding.ASCII.GetString(bytePublicKeyURL); // Get PublicKey from the URL ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(validateServerCertificate); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strAsciiPublickeyURL); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); StreamReader srPublicKey = new StreamReader(response.GetResponseStream(), Encoding.UTF8); this.strPublicKeyBase64 = srPublicKey.ReadToEnd(); response.Close(); srPublicKey.Close(); this.strPublicKeyContentBase64 = this.strPublicKeyBase64.Replace("-----BEGIN PUBLIC KEY-----\n", "").Replace("-----END PUBLIC KEY-----", "").Replace("\n", ""); this.strPublicKeyContentXML = this.RSAPublicKeyString2XML(this.strPublicKeyContentBase64); // Generate the New Authorization String according to the HttpRequest String[] arrURL; if (this.httpURL.Contains('?')) { arrURL = this.httpURL.Split('?'); this.strAuthSourceForMD5 = String.Format("{0}?{1}\n{2}", System.Web.HttpUtility.UrlDecode(arrURL[0]), arrURL[1], this.httpBody); } else { this.strAuthSourceForMD5 = String.Format("{0}\n{1}", System.Web.HttpUtility.UrlDecode(this.httpURL), this.httpBody); } // MD5 hash bytes from the New Authorization String var byteAuthMD5 = byteMD5Encrypt32(this.strAuthSourceForMD5); // Verify Signature System.Security.Cryptography.RSACryptoServiceProvider RSA = new System.Security.Cryptography.RSACryptoServiceProvider(); try { RSA.FromXmlString(this.strPublicKeyContentXML); } catch (System.ArgumentNullException e) { throw new ArgumentNullException(String.Format("VerifySignature Failed : RSADeformatter.VerifySignature get null argument : {0} .", e)); } catch (System.Security.Cryptography.CryptographicException e) { throw new System.Security.Cryptography.CryptographicException(String.Format("VerifySignature Failed : RSA.FromXmlString Exception : {0} .", e)); } System.Security.Cryptography.RSAPKCS1SignatureDeformatter RSADeformatter = new System.Security.Cryptography.RSAPKCS1SignatureDeformatter(RSA); RSADeformatter.SetHashAlgorithm("MD5"); var bVerifyResult = false; try { bVerifyResult = RSADeformatter.VerifySignature(byteAuthMD5, this.byteAuthorizationRequest); } catch (System.ArgumentNullException e) { throw new ArgumentNullException(String.Format("VerifySignature Failed : RSADeformatter.VerifySignature get null argument : {0} .", e)); } catch (System.Security.Cryptography.CryptographicUnexpectedOperationException e) { throw new System.Security.Cryptography.CryptographicUnexpectedOperationException(String.Format("VerifySignature Failed : RSADeformatter.VerifySignature Exception : {0} .", e)); } return bVerifyResult; } public void DoPost() { this.GetPostBody(); // Verify Signature try { if (this.VerifySignature()) { Console.WriteLine("\nVerifySignature Successful . \n"); // do something accoding to callback_body ... this.HttpResponseSuccess(); } else { Console.WriteLine("\nVerifySignature Failed . \n"); this.HttpResponseFailure(); } } catch { Console.WriteLine("\nVerifySignature Failed . \n"); this.HttpResponseFailure(); } }