手机网站进不去怎么解决,百度网站认证官网,wordpress打开速度慢解决办法,wordpress5.1好用一. 简介 简介#xff1a; 上一节中#xff0c;主要介绍了JWT校验#xff0c;它是无状态的#xff0c;是基于Token校验的一种升级#xff0c;它适用的范围很广泛#xff0c;APP、JS前端、后台等等客户端调用服务器端的校验。本节补充几种后台接口的校验方式#xff0c;它…一. 简介 简介 上一节中主要介绍了JWT校验它是无状态的是基于Token校验的一种升级它适用的范围很广泛APP、JS前端、后台等等客户端调用服务器端的校验。本节补充几种后台接口的校验方式它主要适用于后台代码的调用不适合JS、APP等客户端直接调用。 PS在一些对接一些银行接口或者一些支付接口通常会提到这么几个名词 (1). 根据参数名正序排序、根据参数名的ASCII码排序。 (2). appKey和appSecret通常appKey是要当做参数进行传递appSecret用于Sign值的计算通常拼接后用MD5加密有的让你 MD5(拼接参数)然后再和appSecret拼接一块有的直接吧appSecret和其它参数按照一定规则直接拼接最后进行MD5加密。 1. 根据参数名正序排序
eg参数名分别为appKey、abp、userName、userPwd排序先根据首字母排序首字母相同看第二个字母依次类推所以排序的结果为abp、appkey、userName、userPwd我们最终想拼接的字符串的形式为【abphhappkeyhhuserNamehhuserPwdhh】
代码分享
借助orderBy和Select可以实现正序排序然后利用Join方法进行拼接 1 [HttpGet]2 public string TestParamZx()3 {4 Dictionarystring, string dics new Dictionarystring, string();5 dics.Add(abp, hh);6 dics.Add(appkey, hh);7 dics.Add(useName, hh);8 dics.Add(userPwd, hh);9 //根据名称正序排序 拿出来key和value中间用拼接
10 var param dics.OrderBy(u u.Key).Select(u u.Key u.Value);
11 //将param中的集合遍历用拼接成字符串
12 var finalParam string.Join(, param);
13 return finalParam;
14 } 结果 2. 根据参数名的ASCII码由小到大排序 eg参数名分别为1、2、A、a、B、b根据其ASCII排序所以排序的结果为1、2、A、B、a、b我们最终想拼接的字符串的形式为【1hh2hhAhhBhhahhbhh】
代码分享
这里不能直接借助orderBy和Select可以实现ASCII排序需要对orderBy利用CompareOrdinal进行改造 然后利用Join方法进行拼接 1 [HttpGet]2 public string TestParamASCII()3 {4 Dictionarystring, string dics new Dictionarystring, string();5 dics.Add(1, hh);6 dics.Add(2, hh);7 dics.Add(A, hh);8 dics.Add(a, hh);9 dics.Add(B, hh);
10 dics.Add(b, hh);
11 var finalParam GetParamSrc(dics);
12 return finalParam;
13 }
14
15 /// summary
16 /// 参数按照参数名ASCII码从小到大排序字典序
17 /// /summary
18 /// param nameparamsMap/param
19 /// returns/returns
20 public static string GetParamSrc(Dictionarystring, string paramsMap)
21 {
22 //繁琐写法
23 //var vDic paramsMap.OrderBy(x x.Key, new ComparerString()).ToDictionary(x x.Key, y y.Value);
24 //StringBuilder str new StringBuilder();
25 //foreach (KeyValuePairstring, string kv in vDic)
26 //{
27 // string pkey kv.Key;
28 // string pvalue kv.Value;
29 // str.Append(pkey pvalue );
30 //}
31 //string result str.ToString().Substring(0, str.ToString().Length - 1);
32 //return result;
33
34 //简介写法
35 return string.Join(, paramsMap.OrderBy(x x.Key, new ComparerString()).Select(u u.Key u.Value));
36 }
37 public class ComparerString : IComparerString
38 {
39 public int Compare(String x, String y)
40 {
41 return string.CompareOrdinal(x, y);
42 }
43 } 结果 二. 扩展算法1
1.前提 有appKey、appSecret、sign这么几个参数appKey和appSecret事先存在数据库里且一一对应服务商只把appKey和appSecret分发给调用者,调用者采用Get请求的方式除了传递参数外需要在报文头中传递appKey和sign这两个参数其中sign的计算方法为把所有的参数的参数名按照正序排序然后再和appSecret拼接起来一起计算MD5值
即 MD5(abc..appSecret) → MD5(goodId001money150appSecret0806)
服务器端验证见CheckPer1.cs 服务器端调用见SDKClient类
PS这里也可以改成按照参数名的ASCII由小到大排序就换成另外一种算法了。
2.深度分析 这种接口的验证规则适用于后台的代码调用不适用js或其它前端调用比如js调用的话不但组装这个这种格式的参数麻烦而且appSecret就和加密算法就直接暴露在外面了当然你可以对js文件进行混淆来解决这个问题但是这类接口还是更加适合后台代码调用这样的话appSecret保存在服务器端更加安全。 即使appKey和sign这两个参数被截取了也只能发相同数据的请求同一个接口任何一个参数变化sign均会发生变化即验证不过去相同的数据完全可以通过业务代码来限制。
3.举一个使用场景 一个App项目有两个服务器一个是业务服务器一个是下单服务器app项目请求的是业务服务器不能直接请求下单服务器但执行一个下单业务流程如下app采用jwt算法调用业务服务器→业务服务器进行jwt校验→校验通过→对参数进行组装MD5(abc..appSecret)调用下单服务器。
注业务服务器供app调用采用jwt算法下单服务器供业务服务器调用采用上述扩展算法(abc..appSecret)
4. 实战测试 前提appKey为ypf 、appSecret为0806 这里我们就不再做JWT校验了直接通过PostMan调用业务服务器即用PostMan调用:http://localhost:2131/api/Seventh/BuyGoods?goodId001money150 模拟客户端请求。
(1). 业务服务器代码 和封装的SDKClient类(对参数进行拼接发送Get请求) 1 /// summary2 /// 开放给客户端(模拟购买商品接口)3 /// 正常和客户端直接应该有验证比如jwt验证这里省略了,直接用postMan调用4 /// /summary5 /// returns/returns6 [HttpGet]7 public async Taskstring BuyGoods(string goodId, int money)8 {9 //appKey和appScret分别为ypf、0806
10 SDKClient sdk new SDKClient(ypf, 0806);
11 //这里的userId实际应该从jwt中解析出来
12 var payload new Dictionarystring, object
13 {
14 {userId, 1 },
15 {goodId, goodId },
16 {money,money }
17 };
18 SDKResult sdkResult await sdk.GetAsync(http://localhost:2131/api/Seventh/CommitOrder, payload);
19 return sdkResult.Result;
20 } 1 /// summary2 /// 封装请求类3 /// /summary4 public class SDKClient5 {6 private string appKey;7 private string appSecret;8 public SDKClient(string appKey, string appSecret)9 {
10 this.appKey appKey;
11 this.appSecret appSecret;
12 }
13
14 /// summary
15 ///封装发送请求的方法
16 /// /summary
17 /// param nameurl请求地址/param
18 /// param namequeryStringData请求参数键值对/param
19 /// returns/returns
20 public async TaskSDKResult GetAsync(string url, IDictionarystring, object queryStringData)
21 {
22 if (queryStringData null)
23 {
24 throw new ArgumentNullException(queryStringData不能为null);
25 }
26 //根据key的参数名正序排序(首字母有小到大首字母相同看第二个字母)
27 var qsItems queryStringData.OrderBy(kv kv.Key).Select(kv kv.Key kv.Value);
28 //循环遍历qsItems值用拼接起来
29 var queryString string.Join(, qsItems);
30 string finalStr queryString appSecret appSecret;
31 string sign SecurityHelp.CalcMD5(finalStr);
32 using (HttpClient hc new HttpClient())
33 {
34 hc.DefaultRequestHeaders.Add(appKey, appKey);
35 hc.DefaultRequestHeaders.Add(sign, sign);
36 var resp await hc.GetAsync(url ? queryString);
37 SDKResult sdkResult new SDKResult();
38 sdkResult.Result await resp.Content.ReadAsStringAsync();
39 sdkResult.StatusCode resp.StatusCode;
40 return sdkResult;
41 }
42 }
43
44 }
45
46 public class SDKResult
47 {
48 public string Result { get; set; }
49 public HttpStatusCode StatusCode { get; set; }
50 } (2). 过滤器代码和订单服务器代码 1 /// summary2 /// 扩展算法一 的过滤器3 /// /summary4 public class CheckPer1 : AuthorizeAttribute5 {6 public override void OnAuthorization(HttpActionContext actionContext)7 {8 //1.获取报文头中的appKey和sign9 IEnumerablestring appKeys;
10 if (!actionContext.Request.Headers.TryGetValues(appKey, out appKeys))
11 {
12 //HttpContext.Current.Response.Write(报文头中的AppKey为空); //这里不能这么返回用Response.write不能截断仍然会进入到方法中
13 actionContext.Response actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, new HttpError(报文头中的appKey为空));
14 }
15 IEnumerablestring signs;
16 if (!actionContext.Request.Headers.TryGetValues(sign, out signs))
17 {
18 actionContext.Response actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, new HttpError(报文头中的sign为空));
19 }
20 string appKey appKeys.First();
21 string sign signs.First();
22 //2.根据appKey查询数据库获取appSecret
23 //这里进行模拟暂不查询数据库 分别为ypf和0806代替
24 var appInfor new AppInfor()
25 {
26 AppKey ypf,
27 AppSecret 0806,
28 IsEnabled false
29 };
30 if (appInfor null)
31 {
32 actionContext.Response actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, new HttpError(该appKey不存在));
33 }
34 //if (!appInfor.IsEnabled)
35 //{
36 // actionContext.Response actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, new HttpError(该AppKey已被封禁));
37 //}
38
39 //3.计算用户输入参数的连接AppSecret的Md5值
40 //orderedQS就是按照key参数的名字进行排序的QueryString集合
41 var orderedQS actionContext.Request.GetQueryNameValuePairs().OrderBy(kv kv.Key);
42 //拼接keyvalue的数组
43 var segments orderedQS.Select(kv kv.Key kv.Value);
44 //用符号拼接起来
45 string queryString string.Join(, segments);
46 //密钥统一用0806表示
47 string finalStr queryString appSecret 0806;
48 //计算Sign值
49 string computedSign SecurityHelp.CalcMD5(finalStr);
50 //4. 用户传进来md5值和计算出来的比对一下就知道数据是否有被篡改过
51 if (sign.Equals(computedSign, StringComparison.CurrentCultureIgnoreCase))
52 {
53 //表示检验通过
54 }
55 else
56 {
57 actionContext.Response actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, new HttpError(sign校验失败));
58 }
59
60 } 1 /// summary2 /// 模拟订单服务器中的下单接口3 /// 该接口需要采用 扩展算法(一) 的校验4 /// /summary5 /// param namedic/param6 /// returns/returns7 [HttpGet]8 [CheckPer1]9 public string CommitOrder(string userId, string goodId, int money)
10 {
11 var data new
12 {
13 stauts ok,
14 msg userId , goodId , money
15 };
16 return JsonConvert.SerializeObject(data);
17 } (3). 运行结果 三. 扩展算法2
1.前提 有appkey、appSecret、timeStamp这么几个参数其中appkey和appSecret实现存在数据库里且一一对应服务商把appkey和appSecret分发给调用者 调用者采用Post请求的方式提交和返回的数据都为Json格式Http请求的头文件中要加“content-type: application/json”字符编码 统一采用UTF8签名算法如下
finalStrappkeyappSecrettimeStamp)的值全部转换为大写字符
signMD5(finalStr),
每个请求的报文头要有传 appkey、timeStamp(时间戳)、sign(签名)值过来统一校验。时间戳 timestamp是14位标准的时间戳格式时间戳有效期为 10 分钟 服务器时间减时间戳大于 10 分钟的一律视为过期签名会失败。 在服务器端进行验证。
2. 深度分析 这种接口的验证规则适用于后台的代码调用不适用js或其它前端调用比如js调用的话appSecret就和加密算法就直接暴露在外面了非常危险。当然你可以对js文件进行混淆来解决这个问题但是这类接口还是更加适合后台代码调用这样的话appSecret保存在服务器端更加安全。 即使 Aappkey、timeStamp(时间戳)、sign(签名)参数被截取了访问该接口或者其它接口也只能在10分钟能有效。
3.举一个使用场景 一个App项目有两个服务器一个是业务服务器一个是下单服务器app项目请求的是业务服务器不能直接请求下单服务器但执行一个下单业务流程如下app采用jwt算法调用业务服务器→业务服务器进行jwt校验→校验通过→对参数进行组装sign、appkey、timeStamp调用下单服务器。
注业务服务器供app调用采用jwt算法下单服务器供业务服务器调用采用上述扩展算法MD5(appkeyappSecrettimestamp)。
4. 实战测试 前提appKey为ypf 、appSecret为0806 这里我们就不再做JWT校验了直接通过PostMan调用业务服务器即用PostMan调用:http://localhost:2131/api/Seventh/BuyGoods2?goodId001money150 模拟客户端请求 (1). 业务服务器代码 和封装的SDKClient2类(对参数进行拼接发送Post请求) 1 /// summary2 /// 开放给客户端(模拟购买商品接口)3 /// 正常和客户端直接应该有验证比如jwt验证这里省略了,直接用postMan调用4 /// /summary5 /// returns/returns6 [HttpGet]7 public async Taskstring BuyGoods2(string goodId, int money)8 {9 //appKey和appScret分别为ypf、0806
10 SDKClient2 sdk new SDKClient2(ypf, 0806, DateTime.Now.ToString(yyyyMMddhhmmss));
11 //这里的userId正常应该从jwt字符串中解析出来
12 var payload new
13 {
14 userId 1,
15 goodId goodId,
16 money money
17 };
18 SDKResult sdkResult await sdk.PostAsync(http://localhost:2131/api/Seventh/CommitOrder2, payload);
19 return sdkResult.Result;
20 } SDKClient2
(2). 过滤器代码和订单服务器代码 1 /// summary2 ///扩展算法二 的过滤器3 ///换一种新的过滤器写法4 /// /summary5 public class CheckPer2 : FilterAttribute, IAuthorizationFilter6 {7 public async TaskHttpResponseMessage ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, FuncTaskHttpResponseMessage continuation)8 {9 //1.获取Appkey、Timestamp(时间戳)、Sign(签名)
10 IEnumerablestring appKeys;
11 if (!actionContext.Request.Headers.TryGetValues(appKey, out appKeys))
12 {
13 return new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content new StringContent(报文头中的appKey为空) };
14 }
15 IEnumerablestring timestamps;
16 if (!actionContext.Request.Headers.TryGetValues(timeStamp, out timestamps))
17 {
18 return new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content new StringContent(报文头中的timeStamp为空) };
19 }
20 IEnumerablestring signs;
21 if (!actionContext.Request.Headers.TryGetValues(sign, out signs))
22 {
23 return new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content new StringContent(报文头中的sign为空) };
24 }
25 string Appkey appKeys.First();
26 string Timestamp timestamps.First();
27 string Sign signs.First();
28 //2. 计算Timestamp是否过期要注意小时制问题, 需要统一下面的这种方式转换
29 long nowTimeStr long.Parse(DateTime.Now.ToString(yyyyMMddhhmmss));
30 long timeStampStr long.Parse(Timestamp);
31 if (nowTimeStr - timeStampStr 600 || timeStampStr - nowTimeStr 600)
32 {
33 return new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content new StringContent(已过期) };
34 }
35 //3. 计算Sign值是否合法
36 //这里假设秘钥为0806 实际应该根据appKey去数据库中查
37 string AppSecret 0806;
38 string finalStr (Appkey AppSecret Timestamp).ToUpper();
39 string realSign SecurityHelp.CalcMD5(finalStr);
40 if (realSign.Equals(Sign, StringComparison.OrdinalIgnoreCase))
41 {
42 //表示校验通过
43 return await continuation();
44 }
45 else
46 {
47 //表示校验未通过
48 return new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content new StringContent(sign验证失败) };
49 }
50 }
51 } 1 /// summary2 /// 模拟订单服务器中的下单接口3 /// 该接口需要采用 扩展算法(二) 的校验4 /// /summary5 /// param namedic/param6 /// returns/returns7 [HttpPost]8 [CheckPer2]9 public string CommitOrder2(orderData orderData)
10 {
11 var data new
12 {
13 stauts ok,
14 msg orderData.userId , orderData.goodId , orderData.money
15 };
16
17 return JsonConvert.SerializeObject(data);
18 } (3). 运行结果