go语言csrf库使用实现原理示例解析

目录
  • 引言
  • csrf小档案
  • 一、CSRF及其实现原理
    • CSRF攻击示例
  • 二、如何预防
  • 三、CSRF包的使用及实现原理
    • csrf包的安装
    • 基本使用
    • 使用net/http包启动的服务
    • echo框架下使用csrf包
    • gin框架下使用csrf包
    • beego框架下使用csrf包
  • 实现原理
    • csrf结构体
    • csrf包的工作流程
    • 为什么GET、HEAD、OPTIONS、TRACE的请求方法不需要token验证
  • 总结

引言

今天给大家推荐的是web应用安全防护方面的一个包:csrf。该包为Go web应用中常见的跨站请求伪造(CSRF)攻击提供预防功能。

csrf小档案

「csrf小档案」      
star 837 used by -
contributors 25 作者 Gorilla
功能简介 为Go web应用程序和服务提供跨站点请求伪造(csrf)预防功能。可作为gin、echo等主流框架的中间件使用。    
项目地址 github.com/gorilla/csr…    
相关知识 跨站请求伪造(CSRF)、contex.Contex、异或操作    

一、CSRF及其实现原理

CSRF是CROSS Site Request Forgy的缩写,即跨站请求伪造。我们看下他的攻击原理。如下图:

当用户访问一个网站的时候,第一次登录完成后,网站会将验证的相关信息保存在浏览器的cookie中。在对该网站的后续访问中,浏览器会自动携带该站点下的cookie信息,以便服务器校验认证信息。

因此,当服务器经过用户认证之后,服务器对后续的请求就只认cookie中的认证信息,不再区分请求的来源了。那么,攻击者就可以模拟一个正常的请求来做一些影响正常用户利益的事情(比如对于银行来说可以把用户的钱转账到攻击者账户中。或获取用户的敏感、重要的信息等)

相关知识:因为登录信息是基于session-cookie的。浏览器在访问网站时会自动发送该网站的cookie信息,网站只要能识别cookie中的信息,就会认为是认证已通过,而不会区分该请求的来源的。所以给攻击者创造了攻击的机会。

CSRF攻击示例

假设有一个银行网站A,下面的是一个转给账户5000元的请求,使用Get方法

GET https://abank.com/transfer.do?account=RandPerson&amount=$5000 HTTP/1.1

然后,攻击者修改了该请求中的参数,将收款账户更改成了自己的,如下:

GET https://abank.com/transfer.do?account=SomeAttacker&amount=$5000 HTTP/1.1

然后,攻击者将该请求地址放入到一个标签中:

<a href="https://abank.com/transfer.do?account=SomeAttacker&amount=$5000" rel="external nofollow" >Click for more information</a>

最后,攻击者会以各种方式(放到自己的网站中、email、社交通讯工具等)引诱用户点击该链接。只要是用户点击了该链接,并且在之前已经登录了该网站,那么浏览器就会将带认证信息的cookie自动发送给该网站,网站认为这是一个正常的请求,由此,将给黑客转账5000元。造成合法用户的损失。

当然,如果是post表单形式,那么攻击者会将伪造的链接放到form表达中,并用js的方法让表单自动发送:

<body onload="document.forms[0].submit()>
  <form id=”csrf” action="https://abank.com/transfer.do" method="POST">
   <input type="hidden" name="account" value="SomeAttacker"/>
   <input type="hidden" name="amount" value="$5000"/>
 </form>
</body>
<script>
  document.getElementById('csrf').submit();
</script>

二、如何预防

常见的有3种方法:

  • 一种是在网站中增加对请求来源的验证,比如在请求头中增加REFFER信息。
  • 一种是在浏览器中启用SameSite策略。该策略是告诉浏览器,只有请求来源是同网站的才能发送cookie,跨站的请求不要发送cookie。但这种也有漏洞,就是依赖于浏览器是否支持这种策略。
  • 一种是使用Token信息。由网站自己决定token的生成策略以及对token的验证。

其中使用Token信息这种是三种方法中最安全的一种。接下来我们就看看今天要推荐的CSRF包是如何利用token进行预防的。

三、CSRF包的使用及实现原理

csrf包的安装

go get github.com/gorilla/csrf

基本使用

该包主要包括三个功能:

  • 通过csrf.Protect函数生成一个csrf中间件或请求处理器,用于后续的生成及校验token的流程。
  • 通过csrf.Token函数,可以在响应中输出当前生成的token值。
  • 通过csrf.TemplateField函数,可以在html模版中输出一个hidden的input,用于在form表单中提交token。

该包的使用很简单。首先通过csrf.Protect函数生成一个中间件或请求处理器,然后在启动web server时对真实的请求处理器进行包装。

我们来看下该包和主流web框架结合使用的实例。

使用net/http包启动的服务

package main
import (
 "fmt"
 "github.com/gorilla/csrf"
 "net/http"
)
func main() {
 muxServer := http.NewServeMux()
 muxServer.HandleFunc("/", IndexHandler)
 CSRF := csrf.Protect([]byte("32-byte-long-auth-key"))
 http.ListenAndServe(":8000", CSRF(muxServer))
}
func IndexHandler(w http.ResponseWriter, r *http.Request) {
    // 获取token值
 token := csrf.Token(r)
    // 将token写入到header中
 w.Header().Set("X-CSRF-Token", token)
 fmt.Fprintln(w, "hello world.Go")
}

echo框架下使用csrf包

package main
import (
 "github.com/gorilla/csrf"
 "net/http"
 "github.com/labstack/echo"
)
func main() {
 e := echo.New()
 e.POST("/", func(c echo.Context) error {
  return c.String(http.StatusOK, "Hello, World!")
 })
    // 使用自定义的CSRF中间件
 e.Use(CSRFMiddle())
 e.Logger.Fatal(e.Start(":8080"))
}
// 自定义CSRF中间件
func CSRFMiddle() echo.MiddlewareFunc {
 csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key"))
 // 这里使用echo的WrapMiddleware函数将csrfMiddleware转换成echo的中间件返回值
 return echo.WrapMiddleware(csrfMiddleware)
}

gin框架下使用csrf包

import (
 "fmt"
 "github.com/gin-gonic/gin"
 "github.com/gorilla/csrf"
 adapter "github.com/gwatts/gin-adapter"
)
//  定义中间件
func CSRFMiddle() gin.HandlerFunc {
 csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key"))
    // 这里使用adpater包将csrfMiddleware转换成gin的中间件返回值
 return adapter.Wrap(csrfMiddleware)
}
func main() {
 r := gin.New()
    // 在路由中使用中间件
 r.Use(CSRFMiddle())
    // 定义路由
 r.POST("/", IndexHandler)
    // 启动http服务
 r.Run(":8080")
}
func IndexHandler(ctx *gin.Context) {
 ctx.String(200, "hello world")
}

beego框架下使用csrf包

package main
import (
 "github.com/beego/beego"
 "github.com/gorilla/csrf"
)
func main() {
 beego.Router("/", &MainController{})
 beego.RunWithMiddleWares(":8080", CSRFMiddle())
}
type MainController struct {
 beego.Controller
}
func (this *MainController) Get() {
 this.Ctx.Output.Body([]byte("Hello World"))
}
func CSRFMiddle() beego.MiddleWare {
 csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key"))
 // 这里使用adpater包将csrfMiddleware转换成gin的中间件返回值
 return csrfMiddleware
}

实际上,要通过token预防CSRF主要做以下3件事情:每次生成一个唯一的token、将token写入到cookie同时下发给客户端、校验token。接下来我们就来看看csrf包是如何实现如上步骤的。

实现原理

csrf结构体

该包的实现是基于csrf这样一个结构体:

type csrf struct {
 h    http.Handler
 sc   *securecookie.SecureCookie
 st   store
 opts options
}

该结构体同时实现了一个ServeHTTP方法:

func (cs *csrf) ServeHTTP(w http.ResponseWriter, r *http.Request)

在Go中,我们知道ServeHTTP是在内建包net/http中定义的一个请求处理器的接口:

type Handler interface {
 ServeHTTP(ResponseWriter, *Request)
}

凡是实现了该接口的结构体就能作为请求的处理器。在go的所有web框架中,处理器本质上也都是基于该接口实现的。

好了,现在我们来分析下csrf这个结构体的成员:

  • 「h」:是一个http.Handler,作为实际处理请求的处理器。该h的来源是经Protect函数返回值包装后的,即开始示例中CSRF(muxServer)中的muxServer。又因为csrf也是一个请求处理器,请求就会先执行csrf的ServeHTTP方法的逻辑,如果通过了,再执行h的ServeHTTP逻辑。
  • 「sc」:类型是*securecookie.SecureCookie,第三方包,该包的作用是对cookie的值进行加密/解密。在调用csrf.Protect方法时,传递的第一个32字节长的参数就是用于该包进行对称加密用的秘钥。下一篇文章我们会详细介绍该包是如何实现对cookie内容进行/加解密的。
  • 「st」:类型是store,是csrf包中定义的一个接口类型。该属性的作用是将token存储在什么地方。默认是使用cookieStore类型。即将token存储在cookie中。
  • 「opts」:Options属性,用于设置csrf的选项的。比如token存储在cookie中的名字,token在表单中的名字等。

这里大家可能有这样一个疑问:csrf攻击就是基于cookie来进行攻击的,为什么还要把token存储在cookie中呢?在一次请求中,会有两个地方存储token:一个是cookie中,一个是请求体中(query中,header中,或form中),当服务端收到请求时,会同时取出这两个地方的token,进而进行比较。所以如果攻击者伪造了一个请求,服务器能接收到cookie中的token,但不能接收到请求体中的token,所以伪造的攻击还是无效的。

csrf包的工作流程

在开始的“使用net/http包启动的服务”示例中,我们先调用了Protect方法,然后又用返回值对muxServer进行了包装。大家是不是有点云里雾里,为什么要这么调用呢?接下来咱们就来分析下Protect这个函数以及csrf包的工作流程。

在使用csrf的时候,首先要调用的就是Protect函数。Protect的定义如下:

func Protect(authKey []byte, opts ...Option) func(http.Handler) http.Handler

该函数接收一个秘钥和一个选项切片参数。返回值是一个函数类型:func(http.Handler) http.Handler。实际的执行逻辑是在返回的函数中。如下:

CSRF := csrf.Protect([]byte("32-byte-long-auth-key"))
http.ListenAndServe(":8000", CSRF(muxServer))
// Protect源码
func Protect(authKey []byte, opts ...Option) func(http.Handler) http.Handler {
 return func(h http.Handler) http.Handler {
  cs := parseOptions(h, opts...)
  // Set the defaults if no options have been specified
  if cs.opts.ErrorHandler == nil {
   cs.opts.ErrorHandler = http.HandlerFunc(unauthorizedHandler)
  }
  if cs.opts.MaxAge < 0 {
   // Default of 12 hours
   cs.opts.MaxAge = defaultAge
  }
  if cs.opts.FieldName == "" {
   cs.opts.FieldName = fieldName
  }
  if cs.opts.CookieName == "" {
   cs.opts.CookieName = cookieName
  }
  if cs.opts.RequestHeader == "" {
   cs.opts.RequestHeader = headerName
  }
  // Create an authenticated securecookie instance.
  if cs.sc == nil {
   cs.sc = securecookie.New(authKey, nil)
   // Use JSON serialization (faster than one-off gob encoding)
   cs.sc.SetSerializer(securecookie.JSONEncoder{})
   // Set the MaxAge of the underlying securecookie.
   cs.sc.MaxAge(cs.opts.MaxAge)
  }
  if cs.st == nil {
   // Default to the cookieStore
   cs.st = &cookieStore{
    name:     cs.opts.CookieName,
    maxAge:   cs.opts.MaxAge,
    secure:   cs.opts.Secure,
    httpOnly: cs.opts.HttpOnly,
    sameSite: cs.opts.SameSite,
    path:     cs.opts.Path,
    domain:   cs.opts.Domain,
    sc:       cs.sc,
   }
  }
  return cs
 }
}

Protect的实现源码起始很简单,就是在一个闭包中初始化了一个csrf结构体。示例中CSRF就是返回来的func(http.Handler) http.Handler函数。再调用CSRF(muxServer),执行初始化csrf结构体的实例,同时将muxServer包装到csrf结构体的h属性上,最后将该csrf结构体对象返回。因为csrf结构体也实现了ServeHTTP接口,所以csrf自然也就是可以处理请求的http.Handler类型了。

当一个请求来了之后,先执行csrf结构体中的ServeHTTP方法,然后再执行实际的http.Handler。以最开始的请求为例,csrf包的工作流程如下:

大致了解了csrf的工作流程后,我们再来分析各个环节的实现。

「生成唯一的token」

在该包中生成随机、唯一的token是通过随机数来生成的。主要生成逻辑如下:

func generateRandomBytes(n int) ([]byte, error) {
 b := make([]byte, n)
 _, err := rand.Read(b)
 // err == nil only if len(b) == n
 if err != nil {
  return nil, err
 }
 return b, nil
}

crypto/rand包中的rand.Read函数可以随机生成指定字节个数的随机数。但这里出的随机数是字节值,如果序列化成字符串则会是乱码。那如何将字节序列序列化成可见的字符编码呢?那就是对字节进行编码。这里使用的是标准库中的encoding/json包。该包能够对各种类型进行可视化编码。如果对字节序列进行编码,本质上是使用了base64的标准编码。如下:

realToken := generateRandomBytes(32)
//编码后,encodeToken是base64编码的字符串
encodeToken := json.Encode(realToken)

「token的存储位置」

生成token之后,token会存储在两个位置:

  • 一个是随响应将token写入cookie中。在cookie中的token将用于下次请求的基准token和请求中携带的token进行比较。该实现是通过csrf中的cookieStore来存储到cookie中的(store类型)。在cookie中name默认是 _gorilla_csrf。同时,通过cookieStore类型存储到cookie的值是经过加密的,加密使用的是securecookie.SecureCookie包
  • 一个是存储在请求的上下文中。存在这里的token是原始token经过转码的,会随着响应下发给客户端,以便下次请求时随请求体一起发送。该实现是通过context.ValueContext存储在请求的上下文中。

生成token后为什么要存在cookie中呢?CSRF的攻击原理不就是基于浏览器自动发送cookie造成的吗?攻击者伪造的请求还是会直接从cookie中获取token,附带在请求中不就行了吗?答案是否定的。在请求中保存的token,是经过转码后的,跟cookie中的token不一样。在收到请求时,再对token进行解码,然后再和cookie中的token进行比较。看下下面的实现:

func mask(realToken []byte, r *http.Request) string {
 otp, err := generateRandomBytes(tokenLength)
 if err != nil {
  return ""
 }
 // XOR the OTP with the real token to generate a masked token. Append the
 // OTP to the front of the masked token to allow unmasking in the subsequent
 // request.
 return base64.StdEncoding.EncodeToString(append(otp, xorToken(otp, realToken)...))
}

这里我们看到,先生成一个和token一样长度的随机值otp,然后让实际的realToken和opt通过xorToken进行异或操作,将异或操作的结果放到随机值的末尾,然后再进行base64编码产生的。

假设一个token是32位的字节,那么最终的maskToken由64位组成。前32位是otp的随机值,后32位是异或之后的token。两个组合起来就是最终的maskToken。如下图:

这里利用了异或操作的原理来进行转码和解码。我们假设A ^ B = C。那么会有 A = C ^ B

所以,要想还原异或前的真实token值,则从maskToken中取出前32个字节和后32字节,再进行异或操作就能得到真实的token了。然后就可以和cookie中存储的真实的token进行比较了。同时因为经过异或转码的token,攻击者想要进行伪造就很难了。

「输出token」

在上述我们已经知道经过异或操作对原始token进行了转码,我们叫做maskToken。该token要下发给客户端(HEADER、form或其他位置)。那么,客户端用什么字段来接收呢?

默认情况下,maskToken是存储在以下位置的:

  • 若在HEADER头中,则保存在名为  X-CSRF-Token 的字段中。
  • 若在form表单,则保存在名为 gorilla.csrf.Token 的input中。

当然,我们在初始化csrf的实例时,可以指定保存的位置。例如,我们指定HEADER头中的字段名为 X-CSRF-Token-Request中,则可以使用如下代码:

csrf.Protect([]byte("32-byte-long-auth-key"),
             RequestHeader("X-CSRF-Token-Request"))

csrf中可以指定的选项如下:

  • RequestHeader选项函数:指定在HEADER中存储token的字段名称。
  • FieldName选项函数:指定form表中存储token的input的name
  • MaxAge选项函数:指定cookie中值的有效期
  • Domain选项函数:指定cookie的存储域名
  • Path选项函数:指定cookie的存储路径
  • HttpOnly选项函数:指定cookie的值只能在服务端设置,禁止在客户端使用javascript修改
  • SameSite选项函数:指定cookie的SameSite属性
  • ErrorHandler选项函数:指定当token校验不通过或生成token失败时的错误响应的handler

「更新token」

在调用csrf.ServeHTTP函数中,每次都会生成一个新的token,存储在对应的位置上,同时下发给客户端,以便该请求的后续请求携带token值给服务端进行验证。所以,该请求之前的token也就失效了。

为什么GET、HEAD、OPTIONS、TRACE的请求方法不需要token验证

在csrf包中,我们还看到有这么一段判断逻辑:

// Idempotent (safe) methods as defined by RFC7231 section 4.2.2.
safeMethods = []string{"GET", "HEAD", "OPTIONS", "TRACE"}
if !contains(safeMethods, r.Method) {
 //这里进行token的校验
}

为什么GET、HEAD、OPTIONS、TRACE方法的请求不需求token验证呢?因为根据RFC7231文档的规定,这些方法的请求本质上是一种 幂等 的访问方法,这说明开发web的时候g这些请求不应该用于修改数据库状态,而只作为一个请求访问或者链接跳转。通俗地讲,发送一个GET请求不应该引起任何数据状态的改变。用于修改状态更加合适的是post方法,特别是对用户信息状态改变的情况。

所以,如果严格按照RFC的规定来开发的话,这些请求不应该修改数据,而只是获取数据。获取数据对于攻击者来说也没实际价值。

总结

CSRF攻击是基于将验证信息存储于cookie中,同时浏览器在发送请求时会自动携带cookie的原理进行的。所以,其预防原理也就是验证请求来源的真实性。csrf包就是利用了token校验的原理,让前后连续的请求签发token、下次请求验证token的方式进行预防的。

以上就是go语言csrf库使用实现原理示例解析的详细内容,更多关于go语言csrf库使用原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringSecurity解决POST方式下CSRF问题

    目录 解决方案 ①(后端常用): 解决方案②: 解决方案③: 解决方案④: 问题现象:HTTP Status 403-Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN' 原因:Spring Security为防止CSRF(Cross-site requetst forgery跨站请求伪造)的发生,限制了除了get以外的大多数方法. 解决方案 ①(后端常用): 屏

  • 解析django的csrf跨站请求伪造

    目录 1.什么是跨站请求伪造 2.如何规避跨站请求伪造(csrf校验) 3.如何符合csrf校验 3.1 form表单如何符合校验 3.2 ajax如何符合校验 4.csrf相关的装饰器 4.1 针对FBV的csrf装饰器 4.2 针对CBV的csrf装饰器 1.什么是跨站请求伪造 请看图: 我们自行写了一个网站模仿中国银行,用户不知道是否是真的中国银行,并且提交了转账信息,生成一个form表单,向银行服务器发送转账请求,这个form表单和正规银行网站的form表单一模一样,只不过里面隐藏着改变

  • C# 关于爬取网站数据遇到csrf-token的分析与解决

    需求 某航空公司物流单信息查询,是一个post请求.通过后台模拟POST HTTP请求发现无法获取页面数据,通过查看航空公司网站后,发现网站使用避免CSRF攻击机制,直接发挥40X错误. 关于CSRF 读者自行百度 网站HTTP请求分析 Headers Form Data 在head里包含了cookie 与 x-csrf-token  formdata 里包含了_csrf (与head里的值是一样的). 这里通过查看该网站的JS源代码发现_csrf 来自于网页的head标签里 猜测cookie与

  • CSRF跨站请求伪造漏洞分析与防御

    目录 CSRF 漏洞原理 漏洞危害 防御绕过 漏洞利用 防御措施 总结 CSRF 现在的网站都有利用CSRF令牌来防止CSRF,就是在请求包的字段加一个csrf的值,防止csrf,要想利用该漏洞,要和xss组合起来,利用xss获得该csrf值,在构造的请求中将csrf值加进去,就可以绕过csrf防御,利用该漏洞. 该漏洞与xss的区别:xss是通过执行恶意脚本,获取到用户的cookie等信息,再利用cookie等信息进行绕过登录限制,做一些用户可以做的事. 而csrf是伪造请求,让用户自己执行攻

  • 跨站脚本攻击XSS与CSRF区别方法详解

    目录 引文 简介 XSS与CSRF区别 ONE TWO XSS攻击方法 反射型 存储型 DOM型 XSS攻击危害 结语 引文 上篇文章给大家带来了XML实体注入(XXE)不知道小伙伴们学了后有没有自己去运用,今天给大家带来了一个名为跨站脚本攻击(XSS)的漏洞的介绍. 简介 XSS 攻击全称跨站脚本攻击,XSS 是一种在 web 应用中的安全漏洞,它允许攻击者将代码植入到 web 网站里面,供给其它用户 访问,当用户访问到有恶意代码的网页就会产生 xss 攻击. XSS与CSRF区别 之前讲过C

  • go语言csrf库使用实现原理示例解析

    目录 引言 csrf小档案 一.CSRF及其实现原理 CSRF攻击示例 二.如何预防 三.CSRF包的使用及实现原理 csrf包的安装 基本使用 使用net/http包启动的服务 echo框架下使用csrf包 gin框架下使用csrf包 beego框架下使用csrf包 实现原理 csrf结构体 csrf包的工作流程 为什么GET.HEAD.OPTIONS.TRACE的请求方法不需要token验证 总结 引言 今天给大家推荐的是web应用安全防护方面的一个包:csrf.该包为Go web应用中常见

  • flutter Bloc 实现原理示例解析

    目录 序言 1. 事件流 > 状态流 (中转) 2. 使用 BlocBuilder 实时监听状态变更, 如何实现的呢? 总结 扩展 序言 在flutter开发中,我们使用 bloc 框架,基于状态变更进行响应式开发.本篇文章,小轰将 bloc 核心业务块进行拆解简化,聊一聊它的实现思想,bloc 核心能力分为如下两点: 添加事件 event,将 '事件流' 转换为 '状态流' state 监听 bloc 流,每次 state 状态变更,通知 widget 更新 下面,用自定义Bloc的方式,来给

  • go语言通过结构体生成json示例解析

    目录 通过结构体生成json 通过map生成json json解析到结构体 json解析到map 通过结构体生成json buf, err := json.MarshalIndent(s, "", " ") //格式化编码 package main import ( "encoding/json" "fmt" ) //成员变量名首字母必须大写 type IT struct { Company string `json:&quo

  • Kotlin协程之Flow基础原理示例解析

    目录 引言 一.Flow的创建 二.Flow的消费 1.SafeFlow类 2.AbstractFlow类 3. SafeCollector类 4.消费过程中的挂起 引言 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.collect { delay(1000) withContext(Dispatchers.IO) { Log.d("liduo", "$it") } Log.d(&qu

  • react fiber执行原理示例解析

    目录 为什么要使用fiber,要解决什么问题? fiber是什么? 数据结构 执行单元 浏览器工作: Fiber执行原理 workInProgress tree: currentFiber tree: Effects list: render阶段: 遍历节点过程: 收集effect list: commit阶段: 为什么commit必须是同步的操作的? 为什么要使用fiber,要解决什么问题? 在 react16 引入 Fiber 架构之前,react 会采用递归方法对比两颗虚拟DOM树,找出需

  • useReducer createContext代替Redux原理示例解析

    目录 前言 采用react-redux实现 采用react hooks模拟redux实现 异步action 总结 前言 最近看到很多采用useReducer + createContext 实现一个简易的redux的方案,今天亲自试了一下,发现还是会有一些区别的. 采用react-redux实现 这里使用react-redux 实现一个简单的状态管理例子. App.jsx根组件 import React from 'react'; import { Button } from './Button

  • java LockSupport实现原理示例解析

    目录 引言 LockSupport常见函数 LockSupport.park LockSupport.unpark 引言 前文中了解到AQS借助LockSupport.park和LockSupport.unpark完成线程的阻塞和唤醒,那么LockSupport内部又是怎么实现的?这是一个什么类? LockSupport是用于使用锁阻塞线程的基础实现,是其他同步类的基础,这个类为每个使用它的线程关联一个许可证(有点类似于Semaphore),如果许可证可用,线程调用park方法时会立即返回,线程

  • go sync Once实现原理示例解析

    目录 正文 Once 的实现 使用示例 Once 的一些工作机制 Once 详解 hotpath atomic.LoadUint32 atomic.StoreUint32 Mutex 总结 正文 在很多情况下,我们可能需要控制某一段代码只执行一次,比如做某些初始化操作,如初始化数据库连接等. 对于这种场景,go 为我们提供了 sync.Once 对象,它保证了某个动作只被执行一次. 当然我们也是可以自己通过 Mutex 实现 sync.Once 的功能,但是相比来说繁琐了那么一点, 因为我们不仅

  • 网页资源阻塞浏览器加载的原理示例解析

    目录 正文 测试前环境准备 图片会造成阻塞吗? CSS 加载阻塞 CSS 会阻塞后面 JS 的执行吗? JS 加载阻塞 defer 和 async 动态脚本会造成阻塞吗? DOMContentLoaded 和 onload DOMContentLoaded 遇到脚本 DOMContentLoaded 遇到样式 正文 一个页面允许加载的外部资源有很多,常见的有脚本.样式.字体.图片和视频等,对于这些外部资源究竟是如何影响整个页面的加载和渲染的呢?今天来一探究竟. 如何用 Chrome 定制网络加载

  • OPENMP SECTIONS CONSTRUCT原理示例解析

    目录 前言 编译器角度分析 动态库函数分析 总结 前言 在本篇文章当中主要给大家介绍 OpenMP 当中主要给大家介绍 OpenMP 当中 sections construct 的实现原理以及他调用的动态库函数分析.如果已经了解过了前面的关于 for 的调度方式的分析,本篇文章就非常简单了. 编译器角度分析 在这一小节当中我们将从编译器角度去分析编译器会怎么处理 sections construct ,我们以下面的 sections construct 为例子,看看编译器是如何处理 sectio

随机推荐