はじめに
Gin + Next.jsの勉強をしていてCORSの設定をしていたのですが、何度もCORSエラーが生じて解決するのに1日以上費やしてしまったので、その備忘録として今回の投稿を作成します。
CORSとは
CORSとは簡単に言うと、
悪意のあるウェブサイトが明示的な権限を持たずに他のサイトのデータ (Box APIなど) にアクセスするのを防ぐために、ウェブブラウザで利用されているセキュリティメカニズム
のことです。
問題のソースコード
修正前
package router
import (
"gin-twitter/controllers"
"net/http"
"os"
"github.com/gin-contrib/cors"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
csrf "github.com/utrack/gin-csrf"
)
func NewRouter(uc controllers.IUserController) *gin.Engine {
r := gin.Default()
api := r.Group("/api")
api.Use(gin.Logger())
// CORS
r.Use(cors.New(cors.Config{
AllowOrigins: []string{
os.Getenv("FE_URL"),
},
AllowHeaders: []string{
"Origin",
"Content-Type",
"Accept",
"Access-Control-Allow-Headers",
"X-CSRF-Token",
"Access-Control-Allow-Origin",
},
AllowMethods: []string{
"GET",
"POST",
"PUT",
"DELETE",
"OPTIONS",
},
AllowCredentials: true,
}))
api.Use(sessions.Sessions("mysession", cookie.NewStore([]byte("secret"))))
// CSRF
api.Use(csrf.Middleware(csrf.Options{
// Secret: CSRFトークンの署名に使用するシークレットを設定します。ランダムな強力な文字列を設定してください。
Secret: os.Getenv("SECRET"),
// IgnoreMethods: CSRFトークンを検証しないHTTPメソッドを指定できます。
IgnoreMethods: []string{"GET"},
// ErrorFunc: CSRFトークンが一致しない場合に実行されるハンドラを指定できます。
ErrorFunc: func(c *gin.Context) {
c.JSON(http.StatusForbidden, gin.H{"error": "CSRF token mismatch"})
c.Abort()
},
// TokenGetter: リクエストからCSRFトークンを取得するカスタム関数です。
TokenGetter: func(c *gin.Context) string {
return c.GetHeader("X-CSRF-TOKEN")
},
}))
api.POST("/signup", uc.SignUp)
api.POST("/login", uc.LogIn)
api.GET("/csrf", uc.CsrfToken)
return r
}
修正後
package router
import (
"gin-twitter/controllers"
"net/http"
"os"
"github.com/gin-contrib/cors"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
csrf "github.com/utrack/gin-csrf"
)
func NewRouter(uc controllers.IUserController) *gin.Engine {
r := gin.Default()
r.Use(cors.New(cors.Config{
AllowOrigins: []string{
os.Getenv("FE_URL"),
},
AllowHeaders: []string{
"Origin",
"Content-Type",
"Accept",
"Access-Control-Allow-Headers",
"X-CSRF-Token",
"Access-Control-Allow-Origin",
},
AllowMethods: []string{
"GET",
"POST",
"PUT",
"DELETE",
"OPTIONS",
},
AllowCredentials: true,
}))
api := r.Group("/api")
api.Use(gin.Logger())
// CORS
api.Use(sessions.Sessions("mysession", cookie.NewStore([]byte("secret"))))
// CSRF
api.Use(csrf.Middleware(csrf.Options{
// Secret: CSRFトークンの署名に使用するシークレットを設定します。ランダムな強力な文字列を設定してください。
Secret: os.Getenv("SECRET"),
// IgnoreMethods: CSRFトークンを検証しないHTTPメソッドを指定できます。
IgnoreMethods: []string{"GET"},
// ErrorFunc: CSRFトークンが一致しない場合に実行されるハンドラを指定できます。
ErrorFunc: func(c *gin.Context) {
c.JSON(http.StatusForbidden, gin.H{"error": "CSRF token mismatch"})
c.Abort()
},
// TokenGetter: リクエストからCSRFトークンを取得するカスタム関数です。
TokenGetter: func(c *gin.Context) string {
return c.GetHeader("X-CSRF-TOKEN")
},
}))
api.POST("/signup", uc.SignUp)
api.POST("/login", uc.LogIn)
api.GET("/csrf", uc.CsrfToken)
return r
}
api := r.Group("/api")
を先に記述し、その後にCORSの設定を入れていたことが問題でした。
先にCORSの設定をし、その後でルーティングの設定を行うことでエラーは解消されました。
最後に
ルーティングの順番には注意しましょう。