(资料图片仅供参考)
NetCore服务器对接小程序直传阿里云OSS
问题描述:小程序只支持POST方式上传文件,阿里云生成上传链接只能为PUT上传。小程序得使用前端直传方式。本示例为服务器端签名直传并设置上传回调。
PolicyToken.cs
internal class PolicyToken{public string accessid { get; set; }public string policy { get; set; }public string signature { get; set; }public string dir { get; set; }public string host { get; set; }public string expire { get; set; }public string callback { get; set; }}
CallbackParm.cs
internal class CallbackParam{public string callbackUrl { get; set; }public string callbackBody { get; set; }public string callbackBodyType { get; set; }}
生成 PolicyToken
CallbackBody系统参数就不过多说明,详见上面的官方文档,主要说说自定义参数,在构造 Form 表单的参数时,参数名,可以任意命名,但是有两点要注意:
- 自定义参数的格式,必须是
${x:<占位符>}
,x:
一定不能少- 自定义参数的占位符,就是
${x:<占位符>}
部分,花括号内部的名称,必须全部小写,比如:可以是userName=${x:username}&Age=${x:age}
也可以是username=${x:username}&age=${x:age}
,总之,注意占位符全部小写就行了,参数名,按照任意命名方式都可以,回调接口内注意读取就行了- 可以不用自定义占位符,直接对参数进行赋值,比如:`username=张三&age=20
public string GetPolicyToken(string bucketName, string uploadDir, string callbackUrl, long expireTime) { //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 policyConds = new PolicyConditions(); policyConds.AddConditionItem(PolicyConditions.CondContentLengthRange, 100, 1024 * 1024 * 400); // 指定key policyConds.AddConditionItem(MatchMode.Exact, PolicyConditions.CondKey, uploadDir); //policyConds.AddConditionItem(MatchMode.StartWith, PolicyConditions.CondKey, uploadDir); var policy = _ossClient.GeneratePostPolicy(expireDateTime, policyConds); var policy_base64 = EncodeBase64("utf-8", policy); var signature = ComputeSignature(_aliyunOSSConfig.AccessKeySecret, policy_base64); //callback var callback = new CallbackParam(); callback.callbackUrl = callbackUrl; callback.callbackBody = "filename=${object}&size=${size}&mimeType=${mimeType}"; 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 = _aliyunOSSConfig.AccessKeyId; policyToken.host = $"https://{bucketName}.{_aliyunOSSConfig.Host}"; policyToken.policy = policy_base64; policyToken.signature = signature; policyToken.expire = ToUnixTime(expireDateTime); policyToken.callback = callback_string_base64; policyToken.dir = uploadDir; return JsonConvert.SerializeObject(policyToken); } private static string ToUnixTime(DateTime dtime) { const long ticksOf1970 = 621355968000000000; var expires = ((dtime.ToUniversalTime().Ticks - ticksOf1970) / 10000000L) .ToString(CultureInfo.InvariantCulture); return expires; } private static string ComputeSignature(string key, string data) { using (var algorithm = new HMACSHA1()) { algorithm.Key = Encoding.UTF8.GetBytes(key.ToCharArray()); return Convert.ToBase64String( algorithm.ComputeHash(Encoding.UTF8.GetBytes(data.ToCharArray()))); } } private static string EncodeBase64(string code_type, string code) { string encode = ""; byte[] bytes = Encoding.GetEncoding(code_type).GetBytes(code); try { encode = Convert.ToBase64String(bytes); } catch { encode = code; } return encode; }
回调验签
验签步骤
- 获取
Authoriaztion
的值,进行Base64
解码,得到byte[]
。- 对
x-oss-pub-key-url
的值,进行Base64
解码,得到公钥的url
地址。- 校验公钥
url
地址,防止伪造,公钥地址为:
http://gosspublic.alicdn.com/
https://gosspublic.alicdn.com/
- 根据公钥
url
地址获取公钥内容。- 将
<请求路径>?<参数>\n
或<请求路径>\n
拼接,计算MD5
获取byte[]
(请求路径不包含Host
)。- 基于
RSA
,采用MD5
模型进行验签。- 将验证结果返回给OSS(OSS 仅接收 Json 格式的返回)。
- OSS 会将返回的内容直接返回给前端。
public async Task VerifySignature() { // Get the Authorization Base64 from Request var request = _httpContextAccessor.HttpContext.Request; if (!request.Headers.TryGetValue("Authorization", out var authInfo) || StringValues.IsNullOrEmpty(authInfo)) { return false; } // Decode the Authorization from Request var byteAuth = Convert.FromBase64String(authInfo); // Decode the URL of PublicKey if (!request.Headers.TryGetValue("x-oss-pub-key-url", out var tempPubKeyUrl) || StringValues.IsNullOrEmpty(tempPubKeyUrl)) { return false; } var bytePubKeyUrl = Convert.FromBase64String(tempPubKeyUrl.ToString()); var pubKeyUrl = Encoding.ASCII.GetString(bytePubKeyUrl); // 验证公钥域名 if (!pubKeyUrl.StartsWith("http://gosspublic.alicdn.com/", StringComparison.OrdinalIgnoreCase) && !pubKeyUrl.StartsWith("https://gosspublic.alicdn.com/", StringComparison.OrdinalIgnoreCase)) { return false; } // Get PublicKey from the URL ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate); using var client = _httpClientFactory.CreateClient(); var pubKey = await client.GetStringAsync(pubKeyUrl); var strPublicKeyContentBase64 = pubKey.Replace("-----BEGIN PUBLIC KEY-----\n", "").Replace("-----END PUBLIC KEY-----", "").Replace("\n", ""); var strPublicKeyContentXML = RSAPublicKeyString2XML(strPublicKeyContentBase64); // Generate the New Authorization String according to the HttpRequest var httpURL = request.Path.ToString() + request.QueryString.ToString(); // Read body var dictBody = new Dictionary(); if (request.HasFormContentType) { foreach (var item in request.Form) { dictBody.Add(item.Key, item.Value); } } var httpBody = string.Join("&", dictBody.Select(per => $"{per.Key.UrlEncode(true)}={per.Value.UrlEncode(true)}")); // StreamReader stream = new StreamReader(_httpContextAccessor.HttpContext.Request.Body); // var httpBody = await stream.ReadToEndAsync(); var strAuthSourceForMD5 = string.Empty; if (httpURL.Contains("?")) { var arrURL = httpURL.Split("?"); strAuthSourceForMD5 = string.Format("{0}?{1}\n{2}", System.Web.HttpUtility.UrlDecode(arrURL[0]), arrURL[1], httpBody); } else { strAuthSourceForMD5 = string.Format("{0}\n{1}", System.Web.HttpUtility.UrlDecode(httpURL), httpBody); } // MD5 hash bytes from the New Authorization String var byteAuthMD5 = ByteMD5Encrypt32(strAuthSourceForMD5); // Verify Signature using var RSA = new RSACryptoServiceProvider(); try { RSA.FromXmlString(strPublicKeyContentXML); } catch (ArgumentNullException e) { throw new ArgumentNullException(string.Format("VerifySignature Failed : RSADeformatter.VerifySignature get null argument : {0} .", e)); } catch (CryptographicException e) { throw new CryptographicException(string.Format("VerifySignature Failed : RSA.FromXmlString Exception : {0} .", e)); } RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(RSA); RSADeformatter.SetHashAlgorithm("MD5"); var bVerifyResult = false; try { bVerifyResult = RSADeformatter.VerifySignature(byteAuthMD5, byteAuth); } catch (ArgumentNullException e) { throw new ArgumentNullException(string.Format("VerifySignature Failed : RSADeformatter.VerifySignature get null argument : {0} .", e)); } catch (CryptographicUnexpectedOperationException e) { throw new CryptographicUnexpectedOperationException(string.Format("VerifySignature Failed : RSADeformatter.VerifySignature Exception : {0} .", e)); } return bVerifyResult; } public static byte[] ByteMD5Encrypt32(string password) { string cl = password; using MD5 md5 = MD5.Create(); byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl)); return s; } public static string RSAPublicKeyString2XML(string publicKey) { RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey)); return string.Format("{0} {1} ", Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()), Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned())); } public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; }
StringExtension.cs
public static string UrlEncode(this string content, bool needUpper = false) { if (string.IsNullOrEmpty(content)) { return string.Empty; } if (!needUpper) { return HttpUtility.UrlEncode(content); } var result = new StringBuilder(); foreach (var per in content) { var temp = HttpUtility.UrlEncode(per.ToString()); if (temp.Length > 1) { result.Append(temp.ToUpper()); continue; } result.Append(per); } return result.ToString(); }
链接:阿里云 OSS Web 直传/回调/回调签名验证(.NET/C#/Layui) - 简书 (jianshu.com)
下一篇:最后一页
- NetCore服务器对接小程序直传阿里云OSS 焦点报道
- 绿色出行打卡上岸站 助力考生金榜题名_全球热点
- 塞尔达传说王国之泪你是萨派还是科派任务怎么做[多图]_环球速递
- 加油!少年|多图直击2023年北京高考|每日动态
- 长安汽车回应网传“克扣10%货款”:不实信息,已报案
- 2023年6月7日广东省磷酸二氢钠价格最新行情预测 世界热消息
- 环球速递!可能造成一定损失!北京2区发布冰雹黄色预警
- 仓库人员年终总结报告20__年精选范本一篇|每日资讯
- 视讯!如何综合运用基本面分析和技术分析制定科学的股票投资策略
- 大宗交易:富士达成交198.56万元,折价30.23%(06-07)
- 全球速讯:天通股份(600330)6月7日主力资金净卖出1009.32万元
- 翁源县气象台发布雷雨大风黄色预警【III级/较重】【2023-06-07】
- 手机电视游戏平台推荐 电视激斗平台 环球要闻
- WHD杂谈笔记:张伯礼:奥密克戎难以彻底消灭 天天即时看
- 港股异动 | ASMPT(00522)涨超5% 机构称行业景气回升驱动戴维斯双击
-
NetCore服务器对接小程序直传阿里云OSS 焦点报道
NetCore服务器对接小程序直传阿里云OSS>问题描述:小程序只支持POST方
-
黄豆的功效_世界独家
1 减轻女性更年期综合征症状大豆异黄酮是一种结构与雌激素相似,具有雌
-
绿色出行打卡上岸站 助力考生金榜题名_全球热点
高考前夕,门头沟区城管委在地铁S1线上岸站开展“打卡上岸”活动,邀请
-
热资讯!直击怀柔高考现场!少年,加油!
十年寒窗苦读你们定会金榜题名每个人的一生中都有很多夏天但对今天踏上
-
塞尔达传说王国之泪你是萨派还是科派任务怎么做[多图]_环球速递
塞尔达传说王国之泪你是萨派还是科派任务怎么做?你是萨派还是科派是本
-
2023年河南宝丰县“文化和自然遗产日”系列活动启动
6月6日,在河南省宝丰县人民公园北广场,随着以“加强非遗系统性保护,
-
加油!少年|多图直击2023年北京高考|每日动态
今天(6月7日),2023年全国高考拉开帷幕。据北京考试院消息,今年北京
-
山西一能源公司发生安全事故涉嫌瞒报?当地通报:不存在瞒报问题
”针对上述网传情况,五寨县应急管理局日前通过五寨县政府办公室官方微
-
长安汽车回应网传“克扣10%货款”:不实信息,已报案
6月7日,长安汽车(000625)发布了关于网传《致长安汽车采购部的申诉函
-
2023年6月7日广东省磷酸二氢钠价格最新行情预测 世界热消息
中国报告大厅2023年6月7日广东省磷酸二氢钠价格最新走势监测显示:广州
-
天天热资讯!2023年6月7日上海市硫酸亚铁价格最新行情预测
中国报告大厅2023年6月7日上海市硫酸亚铁价格最新走势监测显示:上海果
-
环球速递!可能造成一定损失!北京2区发布冰雹黄色预警
北京市气象台2023年6月7日10时35分还发布大风蓝色预警信号:预计,7日1
-
23安徽债49今日发布发行公告 全球聚焦
23安徽债49发布发行公告
-
仓库人员年终总结报告20__年精选范本一篇|每日资讯
20__年仓储中心在公司领导重视下,紧紧围绕“安全第一、预防为主”的安
-
天天微速讯:公司个人年终总结范文文档版.docx
在繁忙的工作中不知不觉20__年已经过去,又迎来了新的一年,回顾加入这
-
光大证券收警示函 持续督导纳芯微帝科股份存4宗违规
中国证监会网站昨日公布的江苏证监局关于对光大证券股份有限公司采取出
-
视讯!如何综合运用基本面分析和技术分析制定科学的股票投资策略
基本面分析和技术分析是两种常见的股票投资策略,它们各自有着独特的优
-
付一夫:多地网约车面临过剩风险,如何缓解?
网约车市场的过剩风险主要有两个方面:第一,供给过剩会导致整个行业竞
-
大宗交易:富士达成交198.56万元,折价30.23%(06-07)
2023年6月7日,富士达发生1笔大宗交易,总成交17万股,成交金额198 56
-
全球速讯:天通股份(600330)6月7日主力资金净卖出1009.32万元
截至2023年6月7日收盘,天通股份(600330)报收于9 86元,上涨0 31%,换
-
【天天报资讯】大有能源(600403)6月7日主力资金净卖出161.53万元
截至2023年6月7日收盘,大有能源(600403)报收于4 15元,下跌1 19%,换
-
翁源县气象台发布雷雨大风黄色预警【III级/较重】【2023-06-07】
受强对流云团影响,我县翁城镇出现16m s阵风(7级),预计1小时内我县
-
篮球比赛作文600字-焦点关注
篮球比赛作文600字一群朝气蓬勃的少年。他们的脚下是坚硬的球场,他们
-
手机电视游戏平台推荐 电视激斗平台 环球要闻
手机电视游戏平台是很多的玩家很喜欢的游戏的类型之一,在手机电视游戏
-
今头条!香港金管局:重开10年期政府债券投标平均接纳价为91.85港元
6月7日,香港金融管理局发布机构债券发行计划下重开10年期政府债券的投
广告
X 关闭

会议现场1月10日下午,中原旅游通上线启动仪式在郑州举行。河南省文化和旅游厅党组书记、厅长姜继鼎,厅...
- 河南:“新版”科技创新券正式启用
- 深入学习领会党的二十大精神 加快推动全省制造业数字化转型
- 一年最高可为企业节省50万元研发费用 “新版”科技创新券正式启用
- 超额完成任务 2022年河南新增技能人才403.2万人
- 楼阳生在全省市厅级主要领导干部 学习贯彻党的二十大精神研讨班开班式上强调 扎实推进中国式现代化建设河南实践 交出新时代新征程奋勇争先更加出彩优秀答卷 王凯主持
- 完善方案 加快推进数字政府和“河南链”建设
- 突出重点 夯实责任 全力做好重污染天气应急应对工作
- 在打造国家创新高地和全国重要人才中心中当好领头羊
- 河南省金融开放新突破 境外资本参与省内风投有了“新边界”
- 中原旅游通上线 实现“一部手机畅游河南”
广告
X 关闭

1 减轻女性更年期综合征症状大豆异黄酮是一种结构与雌激素相似,具有雌