GO后端开发的跨域问题及解决方式
什么是跨域问题?
在前后端分离的开发方式中,前端运行起来后会提供一个URL(统一资源定位符)供用户进行访问,前端会调用后台的api接口的URL进行数据处理,这两个URL都包含以下几个部分:
- 通信协议:如常见的http、tcp/ip
- 主机号:常说的host:如localhost(127.0.0.1),或者其他IP地址
- 端口号:服务监听的端口号,常用的8080或其他,
- 资源路径:端口号的内容即路径
当在一个页面发起一个新的请求,该请求的URL和原页面的URL中的通信协议、主机号、端口号中任意一个有不同,就称为跨域访问
如在前端的运行在8080端口,访问的首页是http://localhost:8080,在使用gin开发的api项目中,服务监听端口是http://localhost:8090,两个URL端口不同,便发生了跨域访问。
系统如何判断跨域是否安全?
在监听页面可以看到,发生跨域时,浏览器为安全起见,
- 会首先发送一个OPTIONS类型的请求,该请求是测试类型的,又称之为options嗅探,同时会在header中带上origin,判断是否又跨域请求权限,
- 然后服务器相应Access-Control-Allow-Origin的值,该值会与浏览器的origin值进行匹配,如果能够匹配通过,则表示有跨域访问的权限。
- 跨域访问权限检查通过,会正式发送POST请求。
发生跨域问题的页面
请求返回的为404,请求方式为OPTIONS
如何解决跨域问题
可以在gin服务端,进行全局配置:通过中间件的方式设置全局跨域访问,用以返回Access-Control-Allow-Origin和浏览器进行匹配。
在服务端编写跨域访问中间件,详细内容如下:
func Cors() gin.HandlerFunc {
return func(context *gin.Context) {
method := context.Request.Method
origin := context.Request.Header.Get("Origin")
var headerKeys []string
for k, _ := range context.Request.Header {
headerKeys = append(headerKeys, k)
}
headerStr := strings.Join(headerKeys, ",")
if headerStr != "" {
headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
} else {
headerStr = "access-control-allow-origin, access-control-allow-headers"
}
if origin != "" {
context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
context.Header("Access-Control-Allow-Origin", "*") // 设置允许访问所有域
context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
context.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")
context.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")
context.Header("Access-Control-Max-Age", "172800")
context.Header("Access-Control-Allow-Credentials", "false")
context.Set("content-type", "application/json") //// 设置返回格式是json
}
if method == "OPTIONS" {
context.JSON(http.StatusOK, "Options Request!")
}
//处理请求
context.Next()
}
}
其中的Access-Control-Allow-Origin的设置,表示允许进行跨域访问,*表示可以访问所有域。同时,通过Header方法进行了其他的设置。
最后context.Next()是中间件使用的标准用法,表示继续处理请求。