第十节:进一步扩展两种安全校验方式

一. 简介

简介: 上一节中,主要介绍了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,我们最终想拼接的字符串的形式为:【abp=hh&appkey=hh&userName=hh&userPwd=hh】

代码分享:

借助orderBy和Select可以实现正序排序,然后利用Join方法进行拼接

复制代码

 1        [HttpGet]2         public string TestParamZx()3         {4             Dictionary<string, string> dics = new Dictionary<string, 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,我们最终想拼接的字符串的形式为:【1=hh&2=hh&A=hh&B=hh&a=hh&b=hh】

代码分享:

这里不能直接借助orderBy和Select可以实现ASCII排序,需要对orderBy利用CompareOrdinal进行改造,  然后利用Join方法进行拼接

复制代码

 1         [HttpGet]2         public string TestParamASCII()3         {4             Dictionary<string, string> dics = new Dictionary<string, 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 name="paramsMap"></param>
19         /// <returns></returns>
20         public static string GetParamSrc(Dictionary<string, 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 (KeyValuePair<string, 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 : IComparer<String>
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(a+b+c..+appSecret) → MD5("goodId=001&money=150&appSecret=0806")

服务器端验证:见CheckPer1.cs 服务器端调用见:SDKClient类

PS:这里也可以改成按照参数名的ASCII由小到大排序,就换成另外一种算法了。

2.深度分析

  这种接口的验证规则适用于后台的代码调用,不适用js或其它前端调用,比如js调用的话,不但组装这个这种格式的参数麻烦,而且appSecret就和加密算法就直接暴露在外面了,当然你可以对js文件进行混淆来解决这个问题,但是这类接口还是更加适合后台代码调用,这样的话appSecret保存在服务器端,更加安全。

  即使appKey和sign这两个参数被截取了,也只能发相同数据的请求同一个接口,任何一个参数变化,sign均会发生变化,即验证不过去,相同的数据完全可以通过业务代码来限制。

3.举一个使用场景

  一个App项目,有两个服务器,一个是业务服务器,一个是下单服务器,app项目请求的是业务服务器,不能直接请求下单服务器,但执行一个下单业务,流程如下:app采用jwt算法调用业务服务器→业务服务器进行jwt校验→校验通过→对参数进行组装MD5(a+b+c..+appSecret),调用下单服务器。

注:业务服务器供app调用,采用jwt算法,下单服务器供业务服务器调用,采用上述扩展算法(a+b+c..+appSecret)

4. 实战测试

  前提:appKey为:ypf 、appSecret为:0806, 这里我们就不再做JWT校验了,直接通过PostMan调用业务服务器,即:用PostMan调用:http://localhost:2131/api/Seventh/BuyGoods?goodId=001&money=150 模拟客户端请求。

(1). 业务服务器代码 和封装的SDKClient类(对参数进行拼接,发送Get请求)

复制代码

 1         /// <summary>2         /// 开放给客户端(模拟购买商品接口)3         /// 正常和客户端直接应该有验证,比如jwt验证,这里省略了,直接用postMan调用4         /// </summary>5         /// <returns></returns>6         [HttpGet]7         public async Task<string> 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 Dictionary<string, 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  /// <summary>2     /// 封装请求类3     /// </summary>4     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 name="url">请求地址</param>
18         /// <param name="queryStringData">请求参数,键值对</param>
19         /// <returns></returns>
20         public async Task<SDKResult> GetAsync(string url, IDictionary<string, 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  /// <summary>2     /// 扩展算法一 的过滤器3     /// </summary>4     public class CheckPer1 : AuthorizeAttribute5     {6         public override void OnAuthorization(HttpActionContext actionContext)7         {8             //1.获取报文头中的appKey和sign9             IEnumerable<string> 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             IEnumerable<string> 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             //拼接key=value的数组
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         /// <summary>2         /// 模拟订单服务器中的下单接口3         /// 该接口需要采用 扩展算法(一) 的校验4         /// </summary>5         /// <param name="dic"></param>6         /// <returns></returns>7         [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,签名算法如下:

finalStr=(appkey+appSecret+timeStamp)的值全部转换为大写字符

sign=MD5(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(appkey+appSecret+timestamp)。

4. 实战测试

  前提:appKey为:ypf 、appSecret为:0806, 这里我们就不再做JWT校验了,直接通过PostMan调用业务服务器,即:用PostMan调用:http://localhost:2131/api/Seventh/BuyGoods2?goodId=001&money=150 模拟客户端请求

 (1). 业务服务器代码 和封装的SDKClient2类(对参数进行拼接,发送Post请求)

复制代码

 1         /// <summary>2         /// 开放给客户端(模拟购买商品接口)3         /// 正常和客户端直接应该有验证,比如jwt验证,这里省略了,直接用postMan调用4         /// </summary>5         /// <returns></returns>6         [HttpGet]7         public async Task<string> 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     /// <summary>2     ///扩展算法二 的过滤器3     ///换一种新的过滤器写法4     /// </summary>5     public class CheckPer2 : FilterAttribute, IAuthorizationFilter6     {7         public async Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)8         {9             //1.获取Appkey、Timestamp(时间戳)、Sign(签名)
10             IEnumerable<string> appKeys;
11             if (!actionContext.Request.Headers.TryGetValues("appKey", out appKeys))
12             {
13                 return new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("报文头中的appKey为空") };
14             }
15             IEnumerable<string> timestamps;
16             if (!actionContext.Request.Headers.TryGetValues("timeStamp", out timestamps))
17             {
18                 return new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("报文头中的timeStamp为空") };
19             }
20             IEnumerable<string> 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        /// <summary>2         /// 模拟订单服务器中的下单接口3         /// 该接口需要采用 扩展算法(二) 的校验4         /// </summary>5         /// <param name="dic"></param>6         /// <returns></returns>7         [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). 运行结果

 

 

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

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

相关文章

第十一节:WebApi的版本管理的几种方式

一. 背景和方案 1. 多版本管理的概念 Android 、IOS等 App 存在着多版本客户端共存的问题&#xff1a;App 最新版已经升级到了5.0 了&#xff0c;但是有的用户手机上还运行着 4.8、3.9 甚至2.2 版本的 App&#xff0c;由于早期没有内置升级机制、用户不会升级、用户拒绝升级等…

类加载器分类

类加载器干就是将对应类的.class文件中的二进制流加载到内存空间,只管加载&#xff0c;只要符合文件结构就加载&#xff0c;至于能否运行&#xff0c;它不负责 类加载器的分类 1.启动类加载器 2.扩展类加载器 3.应用程序类加载器 委托机制:双亲委派模型 当一个类加载的过…

第十二节:WebApi自动生成在线Api文档的两种方式

一. WebApi自带生成api文档 1. 说明 通过观察&#xff0c;发现WebApi项目中Area文件夹下有一个HelpPage文件夹&#xff0c;如下图&#xff0c;该文件夹就是WebApi自带的生成Api的方式&#xff0c;如果该文件夹没了&#xff0c;可以通过Nuget安装&#xff1a;Microsoft.AspNet.…

JVM 组成部分

JVM可以分为3大部分&#xff1a;类加载器&#xff0c;运行时数据区和执行引擎。 类加载器负责加载.class 文件 运行时数据区负责存放.class 文件&#xff0c;分配内存。运行时数据区又分为5个部分: 方法区&#xff1a;负责存放.class 文件&#xff0c;方法区里有一块区域是运…

第十三节:Asp.Net Core WebApi基础总结和请求方式-第十八节

一. 基础总结 1.Restful服务改造 Core下的WebApi默认也是Restful格式服务&#xff0c;即通过请求方式(Get,post,put,delete)来区分请求哪个方法&#xff0c;请求的URL中不需要写方法名。 但是我们不喜欢这种方式&#xff0c;所以我们将默认的路由规则 [Route("api/[contr…

第十四节:Asp.Net Core WebApi生成在线文档-第十九节

一. 基本概念 1.背景 使用 Web API 时&#xff0c;了解其各种方法对开发人员来说可能是一项挑战。 Swagger 也称为OpenAPI&#xff0c;解决了为 Web API 生成有用文档和帮助页的问题。 它具有诸如交互式文档、客户端 SDK生成和 API 可发现性等优点&#xff0c;目前有两种实现方…

第十五节:Asp.Net Core MVC和WebApi路由规则的总结和对比-第二十节

一. Core Mvc 1.传统路由 Core MVC中&#xff0c;默认会在 Startup类→Configure方法→UseMvc方法中&#xff0c;会有默认路由&#xff1a;routes.MapRoute("default", "{controllerHome}/{actionIndex}/{id?}"); 等价于 app.UseMvcWithDefaultRoute(); …

第四节:Task的启动的四种方式以及Task、TaskFactory的线程等待和线程延续的解决方案

一. 背景 揭秘&#xff1a; 在前面的章节介绍过&#xff0c;Task出现之前&#xff0c;微软的多线程处理方式有&#xff1a;Thread→ThreadPool→委托的异步调用&#xff0c;虽然也可以基本业务需要的多线程场景&#xff0c;但它们在多个线程的等待处理方面、资源占用方面、线程…

第五节:Task构造函数之TaskCreationOptions枚举处理父子线程之间的关系。

一. 整体说明 揭秘&#xff1a; 通过F12查看Task类的源码(详见下面的截图)&#xff0c;发现Task类的构造函数有有一个参数为&#xff1a;TaskCreationOptions类型&#xff0c;本章节可以算作是一个扩展章节&#xff0c;主要就来研究TaskCreationOptions类的作用。 该类主要用来…

第六节:深入研究Task实例方法ContinueWith的参数TaskContinuationOptions

一. 整体说明 揭秘&#xff1a; 该章节的性质和上一个章节类似&#xff0c;也是一个扩展的章节&#xff0c;主要来研究Task类下的实例方法ContinueWith中的参数TaskContinuationOptions。 通过F12查看TaskContinuationOptions的源码&#xff0c;知道主要有这么几个参数&#xf…

第七节:利用CancellationTokenSource实现任务取消和利用CancellationToken类检测取消异常。

一. 传统的线程取消 所谓的线程取消&#xff0c;就是线程正在执行的过程中取消线程任务。 传统的线程取消&#xff0c;是通过一个变量来控制&#xff0c;但是这种方式&#xff0c;在release模式下&#xff0c;被优化从cpu高速缓存中读取&#xff0c;而不是从内存中读取&#xf…

第八节:Task的各类TaskTResult返回值以及通用线程的异常处理方案。

一. Task的各种返回值-Task<TResult> PS&#xff1a; 在前面章节&#xff0c;我们介绍了Task类开启线程、线程等待、线程延续的方式&#xff0c;但我们并没有关注这些方式的返回值&#xff0c;其实他们都是有返回值的Task<TResult>&#xff0c;然后可以通过Task的…

第九节:深究并行编程Parallel类中的三大方法 (For、ForEach、Invoke)和几大编程模型(SPM、APM、EAP、TAP)

一. 并行编程 1. 区分串行编程和串行编程 ①. 串行编程&#xff1a;所谓的串行编程就是单线程的作用下&#xff0c;按顺序执行。(典型代表for循环 下面例子从1-100按顺序执行) ②. 并行编程&#xff1a;充分利用多核cpu的优势&#xff0c;同时开启多个线程并行执行。(典型代表…

第十节:利用async和await简化异步编程模式的几种写法

一. async和await简介 PS&#xff1a;简介 1. async和await这两个关键字是为了简化异步编程模型而诞生的&#xff0c;使的异步编程更简洁&#xff0c;它本身并不创建新线程&#xff0c;但在该方法内部开启多线程&#xff0c;则另算。 2. 这两个关键字适用于处理一些文件IO操作。…

第十一节:深究用户模式锁的使用场景(异变结构、互锁、旋转锁)

一. 锁机制的背景介绍 本章节&#xff0c;将结合多线程来介绍锁机制&#xff0c; 那么问题来了&#xff0c;什么是锁呢&#xff1f; 为什么需要锁&#xff1f; 为什么要结合多线程来介绍锁呢&#xff1f;锁的使用场景又是什么呢&#xff1f; DotNet中又有哪些锁呢&#xff1f; …

第十三节:实际开发中使用最多的监视锁Monitor、lock语法糖的扩展、混合锁的使用(ManualResetEvent、SemaphoreSlim、ReaderWriterLockSlim)

一. 监视锁(Monitor和lock) 1. Monitor类&#xff0c;限定线程个数的一把锁&#xff08;Synchronized lock是他的语法糖&#xff09;&#xff0c;两个核心方法&#xff1a; Enter&#xff1a;锁住某个资源。 Exit&#xff1a;退出某一个资源。 测试案例&#xff1a;开启5个线…

第十四节: 介绍四大并发集合类并结合单例模式下的队列来说明线程安全和非安全的场景及补充性能调优问题。

一. 四大并发集合类 背景&#xff1a;我们目前使用的所有集合都是线程不安全的 。 A. ConcurrentBag&#xff1a;就是利用线程槽来分摊Bag中的所有数据&#xff0c;链表的头插法,0代表移除最后一个插入的值. (等价于同步中的List) B. ConcurrentStack&#xff1a;线程安全的St…

第十五节:深入理解async和await的作用及各种适用场景和用法

一. 同步VS异步 1. 同步 VS 异步 VS 多线程 同步方法&#xff1a;调用时需要等待返回结果&#xff0c;才可以继续往下执行业务 异步方法&#xff1a;调用时无须等待返回结果&#xff0c;可以继续往下执行业务 开启新线程&#xff1a;在主线程之外开启一个新的线程去执行业…

@PostConstruct注解

PostConstruct是Java自己的注解. PostConstruct该注解被用来修饰一个非静态的void()方法. PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次. PostConstruct在构造函数之后执行,init()方法之前执行. 执行顺序 Constructor >> Autow…

springCloud五大组件--Gateway

SpringCloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 Spring Cloud Gateway 的目标&#…