• 用go实现cors中间件


    目录

    一、概述

    二、简单请求和预检请求

    简单请求

    预检请求

    三、使用go的gin框架实现cors配置

    1、安装

    2、函数


    一、概述

            CORS(Cross-Origin Resource Sharing)是一种浏览器安全机制,用于控制在Web应用程序中不同源(Origin)之间的资源共享。一个源是由协议(例如http或https)、主机(例如 www.example.com)、以及端口(例如80或443)组成的组合。CORS允许服务器定义哪些源可以访问其资源,以及哪些HTTP方法和头部可以在跨源请求中使用。

    要点:

    1. 同源策略(Same-Origin Policy):Web浏览器实施了同源策略,它限制了一个页面中加载的资源只能来自相同的源,以防止而已站点获取用户的敏感信息。这意味着默认情况下,跨域请求是被浏览器禁止的。
    2. 跨源HTTP请求:当一个Web页面需要从不同源的服务器获取数据时,例如通过AJAX请求或嵌入其他站点的资源(如字体、图片或脚本),就会设计到跨源HTTP请求。
    3. CORS解决跨域问题: CORS是一种机制,允许服务器在响应中添加HTTP标头来指示浏览器允许跨域请求。这些标头包括”Access-Control-Allow-Origin“、”Access-Control-Allow-Methods“、”Access-Control-Allow-Headers“等
    4. 简单请求和预检请求:浏览器将跨域HTTP请求分为两种类型:简单请求(Simple Request)和预检请求(Preflight Request)。简单请求就是常见的GET、POST请求,而预检请求时一种用于检查服务器是否支持某些请求的OPTION请求。
    5. 服务器配置:CORS的服务器配置通常在服务器端完成。服务器需要响应OPTIONS请求并在响应头中包含CORS标头来指定允许的来源、HTTP方法和头部。服务器还可以选择性地要求携带凭据(credentials)
    6. 浏览器行为:浏览器在发送跨域请求时会自动附加Origin头,然后根据服务器的响应判断是否允许访问资源。如果CORS设置不正确,浏览器会阻止页面访问响应的数据。

    二、简单请求和预检请求

    简单请求

    1、判定条件

    需要同时满足以下条件,浏览器会认为它是一个简单请求

    • 请求方法属于其中一种:GET、POST、HEAD
    • 请求包仅包含安全的字段,常见字段:Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width
    • 请求头如果包含Content-Type,仅限如下值:text/plain、multipart/form-data、application/x-www-form-urlencoded

    2、简单请求交互

    ①当浏览器判某个跨域请求时简单请求时,会在请求头中自动添加Origin字段

    1. GET /cors HTTP/1.1
    2. Host: example.com
    3. Connection: keep-alive
    4. ...
    5. Referer: http://local.com/index.html
    6. Origin: http://local.com //Origin字段会告诉服务器,是哪个源地址在跨域请求

    ②服务器响应头中应包含Access-Control-Allow-Origin ,当服务器收到请求后,如果允许改请求跨域访问,需要在响应头中添加Access-Control-Allow-Origin字段

    1. HTTP/1.1 200 OK
    2. Date: Tue, 21 Jun 2023 08:03:35 GMT
    3. ...
    4. Access-Control-Allow-Origin: http://local.com
    5. ...
    6. 消息体中的数据

    当浏览器看到服务器允许自己访问后,于是,它就把响应顺利的交给js

    预检请求

    1、对于预检请求,请求步骤如下:

    • 浏览器发送预检请求,询问服务器是否允许
    • 服务器响应是否允许
    • 浏览器发送真实请求
    • 服务器完成真实的响应

    2、步骤请求演示

    要发送的请求包:

    POST /cors HTTP/1.1

    Host: example.com

    Connection: keep-alive ...

    Referer: http://local.com/index.html

    Origin: http://local.com

    {"name": "admin", "age": 20}

    ①浏览器发送预检请求

    1. OPTIONS /cors HTTP/1.1
    2. Host: example.com
    3. ...
    4. Origin: http://local.com
    5. Access-Control-Request-Method: POST
    6. Access-Control-Request-Headers: a, b, content-type

    ps:这并非想要发出的真实请求,请求中不包含响应头和也没有消息体。

    预检请求特征:

    请求方法为OPTIONS 没有请求体

    • 请求头中包含 Origin:请求的源,和简单请求的含义一致
    • Access-Control-Request-Method:后续的真实请求将使用的请求方法
    • Access-Control-Request-Headers:后续的真实请求会改动的请求头

    ②服务器允许

    服务器收到预检请求后,可以检查预检请求中包含的信息,如果允许这样的请求,需要响应下面的消息格式

    1. HTTP/1.1 200 OK
    2. ...
    3. Access-Control-Allow-Origin: http://local.com
    4. Access-Control-Allow-Methods: POST
    5. Access-Control-Allow-Headers: a, b, content-type
    6. Access-Control-Max-Age: 86400
    7. ...

    对于预检请求,不需要响应任何的消息体,只需要在响应头中添加: 

    Access-Control-Allow-Origin:和简单请求一样,表示允许的源 Access-Control-Allow-Methods:表示允许的后续真实的请求方法 Access-Control-Allow-Headers:表示允许改动的请求头 Access-Control-Max-Age:告诉浏览器,多少秒内,对于同样的请求源、方法、头,都不需要再发送预检请求了

    ③浏览器发送真实请求

    预检被服务器允许后,浏览器就会发送真实请求了,上面的代码会发送下面的请求数据

    1. POST /cors HTTP/1.1
    2. Host: example.com
    3. Connection: keep-alive ...
    4. Referer: http://local.com/index.html
    5. Origin: http://local.com
    6. {"name": "admin", "age": 20}

    ④服务器响应真实请求

    1. HTTP/1.1 200 OK
    2. Date: Tue, 21 Jun 2023 08:03:35 GMT
    3. ...
    4. Access-Control-Allow-Origin: http://local.com
    5. ...
    6. 添加用户成功

    三、使用go的gin框架实现cors配置

    使用go的gin框架的话,说白了就是添加一个中间件,Gin官方提供CORS中间件,可以很方便的使用 cors解决跨域问题。

    1、安装

    使用命令安装该中间件

    go get github.com/gin-contrib/cors

    2、函数

    cors中间件提供三个函数,代表三种使用方式,分别是NEWDefaultConfigDefault

    • NEW方式

    可以接收 CORS 中间件的配置项,可通过自定义配置项,满足任意需要跨域的场景。

    1. 示例:
    2. router.Use(cors.New(cors.Config{
    3. AllowOrigins: []string{"https://foo.com"},
    4. AllowMethods: []string{"PUT", "PATCH"},
    5. AllowHeaders: []string{"Origin"},
    6. ExposeHeaders: []string{"Content-Length"},
    7. AllowCredentials: true,
    8. AllowOriginFunc: func(origin string) bool {
    9. return origin == "https://github.com"
    10. },
    11. MaxAge: 12 * time.Hour,
    12. }))
    13. //New 函数接收配置项,返回一个用户自定义的 CORS 中间件,绑定到路由中。

    配置项说明:

    • AllowAllOrigins bool 允许所有请求源。
    • AllowOrigins []string 指定允许请求源的列表,如果列表中存在 *,则允许所有请求源,默认值是 []
    • AllowOriginFunc func(origin string) bool 接收参数 origin,函数体中的验证逻辑返回是否允许跨域请求。该配置项优先级高于 AllowOrigins []string,如果设置该配置项,AllowOrigins []string 配置项的设置被忽略。
    • AllowMethods []string 允许的请求方式,默认值是 GETPOSTPUTPATCHDELETEHEAD,和 OPTIONS
    • AllowHeaders []string 用在对预请求的响应中,指示实际的请求中可以使用哪些 HTTP 请求头。
    • AllowCredentials bool 表示请求附带请求凭据时是否响应请求,例如 cookieHTTP authentication 或客户端 SSL 证书
    • ExposeHeaders []string 可以在响应中显示的请求头。
    • MaxAge time.Duration 指示预请求的结果能被缓存多久。
    • AllowWildcard bool 添加请求源是否允许使用通配符,例如 http://some-domain/*https://api.http://some.*.subdomain.com
    • AllowBrowserExtensions bool 允许使用常用的浏览器的扩展模式。
    • AllowWebSockets bool 允许使用 WebSocket 协议。
    • AllowFiles bool 允许使用 file:// 协议。
    • DefaultConfig方式

    默认设置一些通用配置项,我们可以直接使用,也可以在此基础上添加我们需要的其他配置项。

    1. func DefaultConfig() Config {
    2. return Config{
    3. AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"},
    4. AllowHeaders: []string{"Origin", "Content-Length", "Content-Type"},
    5. AllowCredentials: false,
    6. MaxAge: 12 * time.Hour,
    7. }
    8. }
    • Default方式

    DefaultConfig 方式的基础上,设置 AllowAllOrigins 选项为 true,因为 DefaultConfig 方式默认不允许任意请求源,所以需要单独设置 AllowAllOrigins 选项为 true

    1. func Default() gin.HandlerFunc {
    2. config := DefaultConfig()
    3. config.AllowAllOrigins = true
    4. return New(config)
    5. }

    3、某段配置代码分析

    1. //设置cors中间件
    2. func Cors() gin.HandlerFunc {
    3. return func(c *gin.Context) {
    4. method := c.Request.Method //获取请求包的http请求方法
    5. origin := c.Request.Header.Get("Origin") //获取请求包的head头的Origin参数值
    6. var headerkeys []string
    7. for k := range c.Request.Header {
    8. headerkeys = append(headerkeys, k)
    9. }
    10. headerStr := strings.Join(headerkeys, ", ") //将请求包中head头部分的参数用","连接成字符串
    11. //检查请求头不为空
    12. if headerStr != "" {
    13. //如果请求头不为空,将请求头的键名添加到一个字符串中,以便在设置响应头时包含这些键名。
    14. headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers,%s", headerStr)
    15. } else {
    16. headerStr = "access-control-allow-origin, access-control-allow-headers"
    17. }
    18. //检查请求头中origin是否为空
    19. if origin != "" {
    20. //设置响应头中的"Access-Control-Allow-Origin"字段,允许任何域名访问资源(这是一个简单的CORS策略,实际应用中可能需要更严格的控制)。
    21. c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
    22. c.Header("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE, UPDATE")
    23. c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
    24. c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
    25. c.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒
    26. c.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true
    27. c.Set("content-type", "application/json") // 设置返回格式是json
    28. }
    29. //放行所有OPTIONS方法
    30. if method == "OPTIONS" {
    31. c.JSON(http.StatusOK, "Options Request!")
    32. }
    33. }
    34. }

  • 相关阅读:
    乘法与位运算
    理解Hash表
    vue3使用QuillEditor
    Golang入门(1)—— helloworld 初体验
    H2创建表带注释的语法
    vue2自定义指令及钩子函数
    JVM学习之---了解JIT
    【计算机网络系列】物理层②:信道复用技术(频分复用、时分复用、波分复用及码分复用)
    Java虚拟机(JVM)框架
    Linux开发环境配置
  • 原文地址:https://blog.csdn.net/youuzi/article/details/133045036