跨源资源共享

跨源资源共享(CORS)机制允许服务器标识除了它自己的其它 origin,使得浏览器可以访问加载这些资源; 该机制也用来检查服务器是否允许浏览器发送真实的请求,通过浏览器发送"预检"请求实现,在预检请求头部中有 HTTP 方法和真实请求会用到的头。

hertz 提供 cors 跨域中间件的实现 ,这里的实现参考了 gin 的 cors

安装

go get github.com/hertz-contrib/cors

示例代码

package main

import (
    "time"

    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/hertz-contrib/cors"
)

func main() {
    h := server.Default()
    // CORS for https://foo.com and https://github.com origins, allowing:
    // - PUT and PATCH methods
    // - Origin header
    // - Credentials share
    // - Preflight requests cached for 12 hours
    h.Use(cors.New(cors.Config{
        AllowOrigins:     []string{"https://foo.com"},
        AllowMethods:     []string{"PUT", "PATCH"},
        AllowHeaders:     []string{"Origin"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
        AllowOriginFunc: func(origin string) bool {
            return origin == "https://github.com"
        },
        MaxAge: 12 * time.Hour,
    }))
    h.Spin()
}

预检请求

对于跨源访问来说,如果是简单请求,本质上就是在 HTTP 请求头信息中添加一个 Origin 字段,用于描述本次请求来自哪个源,服务端可以直接响应。

而对于非简单跨源访问请求来说(比如请求方法是 PUTPATCHContent-Type 字段类型是 application/json 等),会在正式通信之前,发送一次 HTTP 预检请求(preflight),用于校验客户端是否有跨源资源访问权限,预检请求使用的方法是 OPTIONS,且这是浏览器自发的行为。

注意:部分 hertz-cors 的配置只有在预检请求发生时才会生效。

配置

Hertz 通过使用 cors 中间件,为客户端提供了跨源资源访问的能力。用户可以通过自定义 Config 结构的配置参数,精细控制服务端资源允许跨源访问的范围,亦或选择 hertz-cors 的默认配置,允许来自任意 origin 的客户端访问资源。

上述示例代码中只配置了部分可选参数,Config 的完整参数列表如下:

参数介绍
AllowAllOrigins用于设置允许来自任意 origin 的客户端访问服务端资源,默认为 false
AllowOrigins用于设置允许跨源访问的 origin 列表,默认为 []
AllowOriginFunc用于设置校验客户端 origin 的函数,当启用这个配置时,AllowOrigins 的内容将被忽略
AllowMethods用于设置允许客户端跨源访问所使用的 HTTP 方法列表(在接收到预检请求时生效)
AllowHeaders用于设置客户端发起非简单的跨源资源访问请求时,允许使用的头信息字段列表,默认为 [](在接收到预检请求时生效)
AllowCredentials用于设置允许客户端请求携带用户凭证,如:cookies,token,SSL 凭证,默认为 false
ExposeHeaders用于设置允许暴露给客户端的响应头列表,默认为 []
MaxAge用于设置预检请求的有效期(有效期内不会发起重复的预检请求)
AllowWildcard用于设置允许含通配符的 origin 访问资源,默认为 false
AllowBrowserExtensions用于设置允许使用流行的浏览器扩展模式,默认为 false
AllowWebSockets用于设置允许使用 WebSocket 协议,默认为 false
AllowFiles用于设置允许使用 file:// 协议(危险)除非你能确保 100% 的安全,才可以使用它,默认为 false

AllowAllOrigins

该参数设置为 true 之后,将允许来自任意 origin 的客户端跨源访问服务端资源。

AllowAllOrigins 配置为 true 时, AllowOriginFunc 以及 AllowOrigins 配置不可以使用,否则将发生冲突。

AllowOrigins

描述了可以跨源访问的 origin 列表,如果列表中的任何 origin 携带通配符 * (每个 origin 内只允许使用一个通配符 *),则允许任何满足匹配逻辑的 origin 访问。

AllowAllOrigins 配置冲突,同时只能配置一项。

AllowOriginFunc 配置同时使用时,AllowOriginFunc 的优先级高于 AllowOrigins

若需要使用携带通配符的 origin,则 AllowWildcard 参数需同时设置为 true

示例代码1:

package main

import (
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/hertz-contrib/cors"
)

func main() {
    h := server.Default()
    h.Use(cors.New(cors.Config{
        AllowOrigins:     []string{"https://foo.com"},
    }))
    h.Spin()
}

示例代码2:

package main

import (
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/hertz-contrib/cors"
)

func main() {
    h := server.Default()
    h.Use(cors.New(cors.Config{
        AllowWildcard: 	  true,
        AllowOrigins:     []string{"http://some-domain/*"},
    }))
    h.Spin()
}

AllowOriginFunc

以 origin 为形参,用于自定义 origin 的校验逻辑,返回 true 表示校验通过。

AllowAllOrigins 配置冲突,同时只能配置一项。

AllowOrigins 配置同时使用时,AllowOriginFunc 的优先级高于 AllowOrigins

函数签名:

func(origin string) bool

示例代码:

package main

import (
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/hertz-contrib/cors"
)

func main() {
    h := server.Default()
    h.Use(cors.New(cors.Config{
        AllowOriginFunc: func(origin string) bool {
            return origin == "https://github.com"
        },
    }))
    h.Spin()
}

AllowMethods

该配置只有在接收到预检请求时才会生效,用于设置允许客户端跨源访问所使用的 HTTP 方法列表。

如果是由 GET 或者 POST 发起的简单请求,则无需额外设置。

示例代码:

package main

import (
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/hertz-contrib/cors"
)

func main() {
    h := server.Default()
    h.Use(cors.New(cors.Config{
        AllowWildcard:    true,
        AllowMethods:     []string{"PUT", "PATCH"},
    }))
    h.Spin()
}

AllowHeaders

该配置只有在接收到预检请求时才会生效,如果浏览器请求包括 Access-Control-Request-Headers 字段,则 Access-Control-Allow-Headers 字段是必需的。它是一个逗号分隔的字符串,表明服务器支持的所有头信息字段。

示例代码:

package main

import (
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/hertz-contrib/cors"
)

func main() {
    h := server.Default()
    h.Use(cors.New(cors.Config{
        AllowHeaders:     []string{"Origin"},
    }))
    h.Spin()
}

ExposeHeaders

用于设置允许客户端从 HTTP Response 的 Header 中获取的自定义头信息字段名称。

示例代码:

package main

import (
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/hertz-contrib/cors"
)

func main() {
    h := server.Default()
    h.Use(cors.New(cors.Config{
        ExposeHeaders:    []string{"Content-Length"},
    }))
    h.Spin()
}

更多用法示例详见 cors