This is the first day of my participation in the Gwen Challenge in November. Check out the details: the last Gwen Challenge in 2021

takeaway

In 1202 years ago, there was a very standard solution for cross-domain access. Today, let’s take a look at how to properly start the cross-domain journey in Golang.

The same-origin policy

Before we talk about cross-domain, let’s briefly look at why cross-domain is required. Same-origin Policy requirements are essentially browser security requirements. The same origin policy was first proposed by the browser manufacturer Netscape, and now it has become the most basic and core security function of the browser. The so-called homology is the domain name, protocol, port is completely the same, for example, a.com and https://a.com are not homology…

One of the consequences of the same-origin policy is that Ajax requests can only be same-origin, leading to a plethora of cross-domain request schemes.

This article mainly describes the CORS scheme. For other cross-domain schemes, please refer to reference 1

Note that cross-source is the same as cross-domain in the following, and same-origin is the same as same-domain. In the reference documents, different documents are called slightly differently.

Summary of CORS

The cross-source-domain resource sharing mechanism allows Web application servers to control cross-source access and secure cross-source data transfer. Cross-origin Resource Share (CORS) is a W3C standard that allows browsers to issue XMLHttpRequest requests to cross-source servers, breaking the limitation that Ajax can only use same-origin services.

CORS allows the server to declare which source sites have access to which resources through the browser. For HTTP requests that may cause side effects to the server, such as POST and DELETE requests, the browser must first initiate a prelight request using OPTIONS. To know whether the server will allow the cross-domain request. And in the return of the precheck request, the server can also inform the client whether it needs to carry identity credentials, such as cookies.

The browser generates an error when a CORS request fails, but for security reasons, there is no way to know exactly what is wrong at the JavaScript code level. You can only look at your browser’s console to see exactly what went wrong. This requires extra attention, especially when locating the problem of the service interface, you can only see the interface error, but you can not see the specific error message. If you directly go to check whether the service has a problem, it will be a long way to waste time.

For a fuller introduction, see reference 2 and reference 3

CORS implementation mechanism

The CORS standard adds a new set of HTTP header fields to implement this mechanism. The server controls whether to allow the cross-domain request by setting the corresponding header field. If the server does not return the correct response header, the requester does not receive any data.

Introduction to core Headers

The request Header

top value instructions
Origin Which source does the request come from (Protocol + domain name + port) Based on this value, the server decides whether to approve the request or not. Both simple and precheck requests contain this Header
Access-Control-Request-Method List of request methods When prechecking a request, this field is required to list which HTTP methods the browser’s CORS request will use
Access-Control-Request-Headers Additional Headers This field is a comma-delimited string that specifies the additional header fields that the browser will send for CORS requests

The response headers

top value instructions
Access-Control-Allow-Origin This field is required. Its value is either when requestedOriginThe value of the field, or one*To accept requests from any domain name. Every response must contain.
Access-Control-Allow-Credentials This field is optional. Its value is a Boolean value indicating whether cookies are allowed to be sent. The default is false and no cookies are sent. Set totrue, that is, the server explicitly approves that cookies can be included in the request and sent to the server together. This value can only be set totrueIf the server does not want the browser to send cookies, delete this field. In addition, when the field istrueWhen,Access-Control-Allow-OriginFields cannot be*, can only be the value of the Origin field.
Access-Control-Expose-Headers This field is optional. When CORS requests,XMLHttpRequestThe object’sgetResponseHeader()The method only gets six basic fields:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma. If you want to get the other fields, you have to be inAccess-Control-Expose-HeadersSpecify inside.
Access-Control-Allow-Methods Precheck request return field, required, whose value is a comma-separated string indicating all methods supported by the server for cross-domain requests All supported methods are returned, not just the one requested by the browser. This is to avoid multiple “pre-check” requests.
Access-Control-Allow-Headers Precheck the request return field if the browser request includes itAccess-Control-Request-HeadersField,Access-Control-Allow-HeadersFields are required. It is also a comma-separated string indicating all header information fields supported by the server, not limited to those requested by the browser in precheck.
Access-Control-Max-Age Precheck request return field, optional, that specifies the validity period of this precheck request in seconds. Allow caching of this responseAccess-Control-Max-AgeSeconds, during which another precheck request is not issued.

Practical cases

Cross-domain Settings are a very common requirement in real engineering projects, so let’s take a look at the support provided by common Web frameworks.

gin

Gin framework does not officially provide an out-of-the-box middleware, usually by the user to define a middleware implementation.

A simple implementation example is as follows:

func Cors(a) gin.HandlerFunc {
   return func(c *gin.Context) {
      method := c.Request.Method
      origin := c.Request.Header.Get("Origin") // Request headers
      iforigin ! ="" {
         // When the access-Control-allow-credentials value is true, replace * with the specified domain name
         c.Header("Access-Control-Allow-Origin"."http://a.com") 
         c.Header("Access-Control-Allow-Methods"."POST, GET, OPTIONS, PUT, DELETE, UPDATE")
         c.Header("Access-Control-Allow-Headers"."Origin, X-Requested-With, X-Extra-Header, Content-Type, Accept, Authorization")
         c.Header("Access-Control-Expose-Headers"."Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
         c.Header("Access-Control-Allow-Credentials"."true")
         c.Header("Access-Control-Max-Age"."86400") / / is optional
         c.Set("content-type"."application/json") / / is optional
      }

      if method == "OPTIONS" {
         c.AbortWithStatus(http.StatusNoContent)
      }

      c.Next()
   }
}


func main(a) {
  r := gin.Default()
  r.Use(Cors()) // Enable middleware to allow cross-domain requests
  // Other route Settings
  r.run()
}
Copy the code

The sample code does not perform more logical authentication on Origin. For more complex service scenarios, more logical authentication needs to be performed based on the actual situation.

Gin does not officially offer a cORS package out of the box, but despite its flaws, gin is still a high quality and high performance Web framework worth learning and using.

Note that the cross-domain middleware must be enabled before the routing is set; otherwise, it will not take effect.

Here’s a little exercise for why setting cross-domain doesn’t work.

r := gin.Default()

fileGroup := r.Group("file")
{
    fileGroup.POST("/upload", Upload)
}

r.Use(middlewares.Cors()) // Why does TODO not work?

Copy the code

beego

Compared to GIN, Beego provides a more systematic and powerful solution, with cORS packages available out of the box.

Take a look at the official example

import (
 		"github.com/beego/beego/v2"
		"github.com/beego/beego/v2/server/web/filter/cors"
)
func main(a) {
		// CORS for https://foo.* origins, allowing:
		// - PUT and PATCH methods
		// - Origin header
		// - Credentials share
		beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
			AllowOrigins:     []string{"https://*.foo.com"},
			AllowMethods:     []string{"PUT"."PATCH"},
			AllowHeaders:     []string{"Origin"},
			ExposeHeaders:    []string{"Content-Length"},
			AllowCredentials: true,
		}))
		beego.Run()
}
Copy the code

It can be seen that the CORS package provided by Beego not only has complete encapsulation, but also has flexible configuration mode. For example, AllowOrigin can also use fuzzy matching when the Credentials are true, which greatly enhances the cross-domain authentication capability of the server.

More usage methods can be directly through the source of the official code or documents to learn.

echo

Like Beego, the Echo programming framework also provides the official CORS package, and also provides rich and powerful configuration capabilities.

Use the sample

e := echo.New()
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
  AllowOrigins: []string{"https://a.com"."https://a.net"},
  AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
}))
Copy the code

More usage can also be found in the official code, or refer to the documentation. Note that this is the documentation for the v3 version, and the latest version of Echo is v4.

The resources

  1. www.ruanyifeng.com/blog/2016/0…
  2. Developer.mozilla.org/zh-CN/docs/…
  3. Segmentfault.com/a/119000001…