我正在try 进行一次简单的登录.我遵循了一个关于Udemy的教程.它使用一个渲染函数来渲染模板.我决定以不同的方式处理模板,而不是拥有呈现功能,并改变了周围的一切.但我没有碰PostLoginHandler.自从我做了这个更改后,我的"/LOGIN"帖子就停止工作了.我收到了"400个糟糕的请求",但我不知道为什么.我将包括我认为重要的代码.我会说,我认为这与请求和/或CRSF令牌有关,教程补充说,自从更改后我就再也没有使用过.我没有使用template.Must(template.ParseGlob("./templates/*.tmpl"))来处理模板.

另外,我很抱歉发了这么长的帖子,我不确定需要什么信息.提前感谢您的回复.

旧的渲染功能.

func RenderTemplate(w http.ResponseWriter, r *http.Request, t string, pd *models.PageData) {
    var tmpl *template.Template
    var err error
    _, inMap := tmplCache[t]
    if !inMap {
        err = makeTemplateCache(t)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println("Template in cache")
        }
    }
    tmpl = tmplCache[t]

    pd = AddCSRFData(pd, r)

    err = tmpl.Execute(w, pd)
    if err != nil {
        fmt.Println(err)
    }
}

路由

mux := chi.NewRouter()
mux.Use(middleware.Recoverer)
mux.Use(NoSurf)
mux.Use(SetupSession)

mux.Post("/login", handlers.Repo.PostLoginHandler)

它甚至没有到达PostLoginHandler,但代码是

func (m *Repository) PostLoginHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("here")

    //strMap := make(map[string]string)
    _ = m.App.Session.RenewToken(r.Context())
    err := r.ParseForm()
    if err != nil {
        log.Fatal(err)
    }
    email := r.Form.Get("email")
    password := r.Form.Get("password")

    form := forms.New(r.PostForm)
    form.HasRequired("email", "password")
    form.IsEmail("email")

    if !form.Valid() {
        err := m.App.UITemplates.ExecuteTemplate(w, "login.page.tmpl", &models.PageData{Form: form})
        if err != nil {
            return
        }
        //render.RenderTemplate(w, r, "login.page.tmpl", &models.PageData{Form: form})
        return
    }
    id, _, err := m.DB.AuthenticateUser(email, password)
    if err != nil {
        m.App.Session.Put(r.Context(), "error", "Invalid Email OR Password")
        http.Redirect(w, r, "/login", http.StatusSeeOther)
        return
    }
    m.App.Session.Put(r.Context(), "user_id", id)
    m.App.Session.Put(r.Context(), "flash", "Valid Login")
    http.Redirect(w, r, "/", http.StatusSeeOther)
    //render.RenderTemplate(w, r, "page.page.tmpl", &models.PageData{StrMap: strMap})
}

最后,HTML是一个简单的表单

<form method="post" action="/login">

   {{/*<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">*/}}

   <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
   <div class="form-floating">
       <input type="email" class="form-control" id="email" name="email" placeholder="name@example.com">
       <label for="email">Email address</label>
   </div>
   <div class="form-floating">
       <input type="password" class="form-control" id="password" name="password" placeholder="Password">
       <label for="password">Password</label>
   </div>
   <div class="checkbox mb-3">
       <label>
           <input type="checkbox" value="remember-me"> Remember me
       </label>
   </div>
   <button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
</form>

推荐答案

400 bad request相当于Nosurf个中间件.

Firstly,您应该在模板中取消对此的注释.因为这是CRSF验证所必需的.

<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">

Secondly,应确保将CSRFToken正确传递到模板中.我希望这是在AddCSRFData功能范围内更新的.

我收到了"400个糟糕的请求",但我不知道为什么.

默认情况下,当发生错误时,包不会记录任何内容.但可以忽略failure handler,以查看导致400 bad request的确切原因.

请参见样例代码

package main

import (
    "fmt"
    "html/template"
    "net/http"

    "github.com/go-chi/chi"
    "github.com/go-chi/chi/middleware"
    "github.com/justinas/nosurf"
)

var formTemplate = `
    <html>
    <body>

        <form method="post" action="/submit">
            <!--  comment this and see error -->
            <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"/>

            <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
            <div class="form-floating">
                <input type="email" class="form-control" id="email" name="email" placeholder="name@example.com">
                <label for="email">Email address</label>
            </div>
            <div class="form-floating">
                <input type="password" class="form-control" id="password" name="password" placeholder="Password">
                <label for="password">Password</label>
            </div>
            <div class="checkbox mb-3">
                <label>
                    <input type="checkbox" value="remember-me"> Remember me
                </label>
            </div>

            <button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
        </form>
    </body>
    </html>
`
var tmpl = template.Must(template.New("t1").Parse(formTemplate))

// FailureFunction
// Overriding the default nosurf failure Handler
func FailureFunction() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Printf("Request Failed. Reason: %v", nosurf.Reason(r))
        http.Error(w, http.StatusText(nosurf.FailureCode), nosurf.FailureCode)
    })
}

// NoSurf
// Setting up the handler with overrriding default nosurf failure Handler
func NoSurf(handler http.Handler) http.Handler {
    obj := nosurf.New(handler)
    obj.SetFailureHandler(FailureFunction()) // Override default failure Handler
    return obj
}

func main() {
    r := chi.NewRouter()

    // Add middleware
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    r.Use(NoSurf)

    r.Get("/", HomeHandler)
    r.Post("/submit", SubmitHandler)

    http.ListenAndServe(":8080", r)
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
    token := nosurf.Token(r) // generating the token

    data := map[string]interface{}{
        "CSRFToken": token, // comment this and see the error
    }
    err := tmpl.Execute(w, data)
    if err != nil {
        http.Error(w, "unable to execute the template", http.StatusInternalServerError)
        return
    }
}

func SubmitHandler(w http.ResponseWriter, r *http.Request) {
    err := r.ParseForm()
    if err != nil {
        http.Error(w, "Bad Request", http.StatusBadRequest)
        return
    }

    if !nosurf.VerifyToken(nosurf.Token(r), r.PostForm.Get("csrf_token")) {
        http.Error(w, "Invalid CSRF Token", http.StatusForbidden)
        return
    }

    w.Write([]byte("success"))
}

希望这能帮助你解决你的问题.

Go相关问答推荐

Golang regexpp:获取带有右括号的单词

具有GRPC的RBAC(基于角色的访问控制)-网关生成的REST风格的API

一种基于时间的Golang函数节制器

如何使用Gorilla WebSockets实现Http.Hijacker&;alexedwards/scs/v2

日志(log)文件不在 golang 的日志(log)目录中

Golang:访问any类型泛型上的字段

Go - 永远停止带有上下文的循环

使用Dockertest进行Golang SQL单元测试的基本设置

当填充通道的函数调用未嵌入 goroutine 时,为什么我会遇到死锁?

GoLang:net.LookupHost 返回重复的 ips

Golang 创建一个带有处理程序的模拟数据库并使用接口调用数据库

加密/椭圆:try 在无效点上进行操作

有没有办法计算枚举中定义的项目总数?

在反向 GORM 中创建查询有一个关系

Go:从 ssl 证书中获取 'subject/unstructeredName' 的值

go:embed 文件扩展名模式

如何在Golang中的差异函数中杀死命令Exec

从另一个没有重复的确定性 int

递归数据 struct 解组在 Go Lang Protobuf 中给出错误无法解析无效的线格式数据

有没有办法停止long blocking 函数?