在本章中,我们将介绍以下配方:
除了创建应用之外,保护 web 应用是最重要的方面之一,我们将在本章中学习。应用安全性是一个非常广泛的主题,可以通过各种方式实现,这超出了本章的范围。
在本章中,我们将只关注如何将我们的 Go web 应用从 HTTP 协议移动到 HTTPS,这通常被称为HTTP+TLS**(传输层安全性),以及使用JSON web 令牌**【JWTs】保护 Go web 应用 REST 端点,保护我们的应用免受跨站点请求伪造(CSRF)攻击。
要将在 HTTP 上运行的服务器移动到 HTTPS,我们必须做的第一件事是获取 SSL 证书,该证书可以是自签名的,也可以是由受信任的证书颁发机构(如 Comodo、Symantec 或 GoDaddy)签名的证书
要获得由可信证书颁发机构签署的 SSL 证书,我们必须向他们提供一个证书签署请求(CSR),该请求主要由密钥对的公钥和一些附加信息组成,而自签名证书是您可以向自己颁发的证书,使用自己的私钥签名。
自签名证书可用于加密数据以及 CA 签名证书,但用户将显示一条警告,表示其计算机或浏览器不信任该证书。因此,您不应该将它们用于生产或公共服务器。
在这个配方中,我们将学习如何创建私钥、证书签名请求和自签名证书。
本配方假设您的机器上安装了openssl
。要验证是否已安装,请执行以下命令:
$ openssl
OpenSSL> exit
openssl
生成私钥和证书签名请求:$ openssl req -newkey rsa:2048 -nodes -keyout domain.key -out domain.csr -subj "/C=IN/ST=Mumbai/L=Andheri East/O=Packt/CN=packtpub.com"
这将产生以下输出:
$ openssl req -key domain.key -new -x509 -days 365 -out domain.crt -subj "/C=IN/ST=Mumbai/L=Andheri East/O=Packt/CN=packtpub.com"
一旦命令执行成功,我们可以看到生成了domain.key
、domain.csr
和domain.crt
,其中domain.key
是用于对 SSL 证书进行签名的 2048 位 RSA 私钥,domain.crt
和domain.csr
是由密钥对的公钥和一些附加信息组成的证书签名请求,签名时插入到证书中的。
让我们了解一下为生成证书签名请求而执行的命令:
-newkey rsa:2048
选项创建一个新的证书请求和一个新的私钥,该私钥应为 2048 位,使用 RSA 算法生成。-nodes
选项指定创建的私钥不会使用密码短语进行加密。-keyout domain.key
选项指定要将新创建的私钥写入的文件名。-out domain.csr
选项指定要写入的输出文件名,或默认为标准输出。-subj
选项用指定数据替换输入请求的主题字段,并输出修改后的请求。如果我们没有指定此选项,那么我们必须通过OpenSSL
回答 CSR 信息提示来完成此过程。接下来,我们将了解为生成证书并使用私钥对其签名而执行的命令,如下所示:
openssl req -key domain.key -new -x509 -days 365 -out domain.crt -subj "/C=IN/ST=Mumbai/L=Andheri East/O=Packt/CN=packtpub.com"
-key
选项指定从中读取私钥的文件。-x509
选项输出自签名证书而不是证书请求。-days 365
选项指定证书的认证天数。默认值为 30 天。
一旦 web 应用开发结束,我们很可能会将其部署到服务器上。在部署时,始终建议在 HTTPS 协议而不是 HTTP 上运行 web 应用,特别是对于公开的服务器。在本食谱中,我们将学习如何在围棋中做到这一点。
https-server.go
,在这里我们将定义一个只写 Hello World 的处理程序!发送到所有 HTTPS 请求的 HTTP 响应流,如下所示:package main
import
(
"fmt"
"log"
"net/http"
)
const
(
CONN_HOST = "localhost"
CONN_PORT = "8443"
HTTPS_CERTIFICATE = "domain.crt"
DOMAIN_PRIVATE_KEY = "domain.key"
)
func helloWorld(w http.ResponseWriter, r *http.Request)
{
fmt.Fprintf(w, "Hello World!")
}
func main()
{
http.HandleFunc("/", helloWorld)
err := http.ListenAndServeTLS(CONN_HOST+":"+CONN_PORT,
HTTPS_CERTIFICATE, DOMAIN_PRIVATE_KEY, nil)
if err != nil
{
log.Fatal("error starting https server : ", err)
return
}
}
$ go run https-server.go
一旦我们运行该程序,HTTPS 服务器将在端口8443
上开始本地侦听。
浏览https://localhost:8443/
会给我们带来你好世界!作为来自服务器的响应:
此外,从命令行执行带curl
的--insecure
标志的GET
请求将跳过证书验证,因为我们使用的是自签名证书:
$ curl -X GET https://localhost:8443/ --insecure
Hello World!
让我们了解一下我们编写的程序:
const (CONN_HOST = "localhost" CONN_PORT = "8443" HTTPS_CERTIFICATE = "domain.crt" DOMAIN_PRIVATE_KEY = "domain.key")
:在这里,我们声明了四个常量-CONN_HOST
的值为localhost
、CONN_PORT
的值为8443
、HTTPS_CERTIFICATE
的值为domain.crt
或自签名证书、DOMAIN_PRIVATE_KEY
的值为domain.key
或我们在之前配方中创建的私钥。func helloWorld(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }
:这是一个 Go 函数,以ResponseWriter
和Request
为输入参数,将Hello World!
写入 HTTP 响应流。接下来,我们声明了main()
程序执行从何处开始。由于这种方法可以做很多事情,让我们逐行理解它:
http.HandleFunc("/", helloWorld)
:在这里,我们使用net/http
包的HandleFunc
使用 URL 模式/
注册helloWorld
函数,这意味着helloWorld
被执行,每当我们访问 HTTPS URL 模式/
时,将(http.ResponseWriter, *http.Request)
作为输入传递给它。err := http.ListenAndServeTLS(CONN_HOST+":"+CONN_PORT, HTTPS_CERTIFICATE, DOMAIN_PRIVATE_KEY, nil)
:在这里,我们调用http.ListenAndServeTLS
为 HTTPS 请求提供服务,这些请求在单独的 Goroutine 中处理每个传入连接。ListenAndServeTLS
接受四个参数服务器地址、SSL 证书、私钥和处理程序。这里,我们将服务器地址传递为localhost:8443
,将自签名证书、私钥和处理程序传递为nil
,这意味着我们要求服务器使用DefaultServeMux
作为处理程序。if err != nil { log.Fatal("error starting https server : ", err) return}
:在这里,我们检查启动服务器是否有问题,如果有,则记录错误并以状态码 1 退出。在编写 RESTful API 时,在允许用户访问之前对用户进行身份验证是非常常见的。对用户进行身份验证的先决条件是创建 API 路由,我们将在本配方中介绍。
go get
命令安装github.com/gorilla/mux
和github.com/gorilla/handlers
包,如下所示:$ go get github.com/gorilla/mux
$ go get github.com/gorilla/handlers
http-rest-api.go
,在这里我们将定义三条路由——/status
、/get-token
和/employees
——以及它们的处理程序,如下所示:package main
import
(
"encoding/json"
"log"
"net/http"
"os"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
const
(
CONN_HOST = "localhost"
CONN_PORT = "8080"
)
type Employee struct
{
Id int `json:"id"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
}
type Employees []Employee
var employees []Employee
func init()
{
employees = Employees
{
Employee{Id: 1, FirstName: "Foo", LastName: "Bar"},
Employee{Id: 2, FirstName: "Baz", LastName: "Qux"},
}
}
func getStatus(w http.ResponseWriter, r *http.Request)
{
w.Write([]byte("API is up and running"))
}
func getEmployees(w http.ResponseWriter, r *http.Request)
{
json.NewEncoder(w).Encode(employees)
}
func getToken(w http.ResponseWriter, r *http.Request)
{
w.Write([]byte("Not Implemented"))
}
func main()
{
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/status", getStatus).Methods("GET")
router.HandleFunc("/get-token", getToken).Methods("GET")
router.HandleFunc("/employees", getEmployees).Methods("GET")
err := http.ListenAndServe(CONN_HOST+":"+CONN_PORT,
handlers.LoggingHandler(os.Stdout, router))
if err != nil
{
log.Fatal("error starting http server : ", err)
return
}
}
$ go run http-rest-api.go
一旦我们运行程序,HTTP 服务器将在端口8080
上开始本地侦听。
接下来,您可以从命令行执行一个GET
请求,如下所示:
$ curl -X GET http://localhost:8080/status
API is up and running
这将为您提供 RESTAPI 的状态。您可以从命令行执行GET
请求,如下所示:
$ curl -X GET http://localhost:8080/employees
[{"id":1,"firstName":"Foo","lastName":"Bar"},{"id":2,"firstName":"Baz","lastName":"Qux"}]
这将为您提供所有员工的列表。我们可以尝试通过命令行获取访问令牌,如下所示:
$ curl -X GET http://localhost:8080/get-token
我们将从服务器获取未实现的消息。
让我们了解一下我们编写的程序:
import ("encoding/json" "log" "net/http" "os" “github.com/gorilla/handlers" "github.com/gorilla/mux")
:这里,我们导入github.com/gorilla/mux
来创建 Gorilla Mux 路由器,github.com/gorilla/handlers
来创建 Gorilla 日志处理程序,用于以 Apache 公共日志格式记录 HTTP 请求。func getStatus(w http.ResponseWriter, r *http.Request) { w.Write([]byte("API is up and running"))}
:这是一个处理程序,它只写 API 启动并运行到 HTTP 响应流。func getEmployees(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(employees)}
:这是一个将静态员工数组写入 HTTP 响应流的处理程序。func notImplemented(w http.ResponseWriter, r *http.Request) { w.Write([]byte(“Not Implemented")) }
:这是一个只将未实现的写入 HTTP 响应流的处理程序。main()
,其中我们使用NewRouter()
处理程序创建了一个gorilla/mux
路由器实例,新路由的尾随斜杠行为为true
,向其添加路由并注册处理程序,最后调用http.ListenAndServe
来服务 HTTP 请求,这些请求在单独的 Goroutine 中处理每个传入连接。ListenAndServe
接受两个参数:服务器地址和处理程序。这里,我们将服务器地址传递为localhost:8080
,处理程序传递为 GorillaLoggingHandler
,它以 Apache 通用日志格式记录 HTTP 请求。为了保护 REST API 或服务端点的安全,您必须在 Go 中编写一个生成 JSON web 令牌的处理程序,或JWT
。
在这个配方中,我们将使用https://github.com/dgrijalva/jwt-go
生成JWT
,尽管您可以从 Go 中提供的许多第三方库中实现任何库,例如https://github.com/square/go-jose
和https://github.com/tarent/loginsrv
。
go get
命令安装github.com/dgrijalva/jwt-go
、github.com/gorilla/mux
和github.com/gorilla/handlers
包,如下所示:$ go get github.com/dgrijalva/jwt-go
$ go get github.com/gorilla/handlers
$ go get github.com/gorilla/mux
create-jwt.go
,在这里我们将定义生成JWT
的getToken
处理程序,如下所示:package main
import
(
"encoding/json"
"log"
"net/http"
"os"
"time"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
const
(
CONN_HOST = "localhost"
CONN_PORT = "8080"
CLAIM_ISSUER = "Packt"
CLAIM_EXPIRY_IN_HOURS = 24
)
type Employee struct
{
Id int `json:"id"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
}
type Employees []Employee
var employees []Employee
func init()
{
employees = Employees
{
Employee{Id: 1, FirstName: "Foo", LastName: "Bar"},
Employee{Id: 2, FirstName: "Baz", LastName: "Qux"},
}
}
var signature = []byte("secret")
func getToken(w http.ResponseWriter, r *http.Request)
{
claims := &jwt.StandardClaims
{
ExpiresAt: time.Now().Add(time.Hour *
CLAIM_EXPIRY_IN_HOURS).Unix(),
Issuer: CLAIM_ISSUER,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, _ := token.SignedString(signature)
w.Write([]byte(tokenString))
}
func getStatus(w http.ResponseWriter, r *http.Request)
{
w.Write([]byte("API is up and running"))
}
func getEmployees(w http.ResponseWriter, r *http.Request)
{
json.NewEncoder(w).Encode(employees)
}
func main()
{
muxRouter := mux.NewRouter().StrictSlash(true)
muxRouter.HandleFunc("/status", getStatus).Methods("GET")
muxRouter.HandleFunc("/get-token", getToken).Methods("GET")
muxRouter.HandleFunc("/employees", getEmployees).Methods("GET")
err := http.ListenAndServe(CONN_HOST+":"+CONN_PORT,
handlers.LoggingHandler(os.Stdout, muxRouter))
if err != nil
{
log.Fatal("error starting http server : ", err)
return
}
}
$ go run create-jwt.go
一旦我们运行程序,HTTP 服务器将在端口8080
上开始本地侦听。
接下来,我们从命令行执行一个GET
请求,如下所示:
$ curl -X GET http://localhost:8080/status
API is up and running
它将为您提供 API 的状态。接下来,我们从命令行执行一个GET
请求,如下所示:
$ curl -X GET http://localhost:8080/employees
[{"id":1,"firstName":"Foo","lastName":"Bar"},{"id":2,"firstName":"Baz","lastName":"Qux"}]
它会给你一份所有员工的名单。接下来,让我们尝试通过命令行获取 REST API 的访问令牌:
$ curl -X GET http://localhost:8080/get-token
它将为我们提供生成的 JWT 令牌:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTM1MDY4ODEsImlzcyI6IlBhY2t0In0.95vuiR7lpWt4AIBDasBzOffL_Xv78_J9rcrKkeqSW08
接下来,浏览到https://jwt.io/
并粘贴编码部分生成的令牌,查看其解码值,如下图所示:
让我们了解一下我们在这个配方中引入的变化:
import ( "encoding/json" "log" "net/http" "os" "time" jwt "github.com/dgrijalva/jwt-go" "github.com/gorilla/handlers" "github.com/gorilla/mux")
:在这里,我们导入了一个额外的包——github.com/dgrijalva/jwt-go
——它具有 JWT 的 Go 实现。const ( CONN_HOST = "localhost" CONN_PORT = "8080" CLAIM_ISSUER = "Packt" CLAIM_EXPIRY_IN_HOURS = 24 )
:在这里,我们引入了两个额外的常量,一个是CLAIM_ISSUER
,用于标识签发 JWT 的委托人,另一个是CLAIM_EXPIRY_IN_HOURS
,用于标识 JWT 不得接受处理的到期时间。var signature = []byte("secret")
:这是服务器持有的签名。使用此功能,服务器将能够验证现有令牌并对新令牌进行签名。接下来,我们定义了一个getToken
处理程序,我们首先使用JWT StandardClaims
处理程序准备了一个 claims 对象,然后使用jwt NewWithClaims
处理程序生成一个 JWT 令牌,最后使用服务器签名对其进行签名并将其写入 HTTP 响应流
一旦有了 RESTAPI 端点和 JWT 令牌生成器处理程序,我们就可以轻松地用 JWT 保护端点,我们将在本配方中介绍。
go get
命令安装github.com/auth0/go-jwt-middleware
、github.com/dgrijalva/jwt-go
、github.com/gorilla/mux
和github.com/gorilla/handlers
包,如下所示:$ go get github.com/auth0/go-jwt-middleware
$ go get github.com/dgrijalva/jwt-go
$ go get github.com/gorilla/handlers
$ go get github.com/gorilla/mux
http-rest-api-secured.go
,在这里我们将定义 JWT 中间件来检查 HTTP 请求上的 JWT,并用它包装/employees
路由,如下所示:package main
import
(
"encoding/json"
"log"
"net/http"
"os"
"time"
jwtmiddleware "github.com/auth0/go-jwt-middleware"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
const
(
CONN_HOST = "localhost"
CONN_PORT = "8080"
CLAIM_ISSUER = "Packt"
CLAIM_EXPIRY_IN_HOURS = 24
)
type Employee struct
{
Id int `json:"id"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
}
type Employees []Employee
var employees []Employee
func init()
{
employees = Employees
{
Employee{Id: 1, FirstName: "Foo", LastName: "Bar"},
Employee{Id: 2, FirstName: "Baz", LastName: "Qux"},
}
}
var signature = []byte("secret")
var jwtMiddleware = jwtmiddleware.New
(
jwtmiddleware.Options
{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error)
{
return signature, nil
},
SigningMethod: jwt.SigningMethodHS256,
}
)
func getToken(w http.ResponseWriter, r *http.Request)
{
claims := &jwt.StandardClaims
{
ExpiresAt: time.Now().Add(time.Hour *
CLAIM_EXPIRY_IN_HOURS).Unix(),
Issuer: CLAIM_ISSUER,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, _ := token.SignedString(signature)
w.Write([]byte(tokenString))
}
func getStatus(w http.ResponseWriter, r *http.Request)
{
w.Write([]byte("API is up and running"))
}
func getEmployees(w http.ResponseWriter, r *http.Request)
{
json.NewEncoder(w).Encode(employees)
}
func main()
{
muxRouter := mux.NewRouter().StrictSlash(true)
muxRouter.HandleFunc("/status", getStatus).Methods("GET")
muxRouter.HandleFunc("/get-token", getToken).Methods("GET")
muxRouter.Handle("/employees", jwtMiddleware.Handler
(http.HandlerFunc(getEmployees))).Methods("GET")
err := http.ListenAndServe(CONN_HOST+":"+CONN_PORT,
handlers.LoggingHandler(os.Stdout, muxRouter))
if err != nil
{
log.Fatal("error starting http server : ", err)
return
}
}
$ go run http-rest-api-secured.go
一旦我们运行程序,HTTP 服务器将在端口8080
上开始本地侦听。
接下来,我们从命令行执行一个GET
请求,如下所示:
$ curl -X GET http://localhost:8080/status
API is up and running
它将为您提供 API 的状态。接下来,我们从命令行执行一个GET
请求,如下所示:
$ curl -X GET http://localhost:8080/employees
Required authorization token not found
它将向我们显示请求中未找到 JWT 的消息。因此,要获取所有员工的列表,我们必须获取 API 的访问令牌,我们可以通过执行以下命令来获取:
$ curl -X GET http://localhost:8080/get-token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTM1MTI2NTksImlzcyI6IlBhY2t0In0.2r_q_82erdOmt862ofluiMGr3O5x5_c0_sMyW7Pi5XE
现在,调用 employee API,再次将 JWT 作为 HTTPAuthorization
请求头传递,如下所示:
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTM1MTI2NTksImlzcyI6IlBhY2t0In0.2r_q_82erdOmt862ofluiMGr3O5x5_c0_sMyW7Pi5XE" http://localhost:8080/employees
它将为您提供所有员工的列表,如下所示:
[{"id":1,"firstName":"Foo","lastName":"Bar"},{"id":2,"firstName":"Baz","lastName":"Qux"}]
让我们了解一下我们在这个配方中引入的变化:
import ( "encoding/json" "log" "net/http" "os" "time" jwtmiddleware "github.com/auth0/go-jwt-middleware" jwt "github.com/dgrijalva/jwt-go" "github.com/gorilla/handlers" "github.com/gorilla/mux")
,我们导入了一个额外的包github.com/auth0/go-jwt-middleware
,别名为jwtmiddleware
,用于检查 HTTP 请求上的 JWT。jwtmiddleware
,将SigningMethod
作为HS256
传递,ValidationKeyGetter
选项作为 Go 函数,返回密钥以验证 JWT。这里,服务器签名用作验证 JWT 的密钥。main()
中用jwtmiddleware
处理程序包装了/employees
路由,这意味着对于 URL 模式为/employees
的每个请求,我们在提供响应之前检查并验证 JWT。保护 web 应用不受恶意网站、电子邮件、博客、即时消息或攻击受信任站点的程序的攻击是一种常见做法,用户当前已通过该站点的身份验证,以防止不必要的操作。我们通常称这种跨站点请求伪造。
使用 Gorilla CSRF 包在 Go 中实现跨站点请求伪造相当容易,我们将在本配方中介绍它。
go get
命令安装github.com/gorilla/csrf
和github.com/gorilla/mux
包,如下所示:$ go get github.com/gorilla/csrf
$ go get github.com/gorilla/mux
sign-up.html
,以及在提交 HTML 表单时调用的操作,如下所示:<html>
<head>
<title>Sign Up!</title>
</head>
<body>
<form method="POST" action="/post" accept-charset="UTF-8">
<input type="text" name="name">
<input type="text" name="email">
{{ .csrfField }}
<input type="submit" value="Sign up!">
</form>
</body>
</html>
prevent-csrf.go
,其中我们创建一个呈现注册 HTML 表单的signUp
处理程序和一个post
处理程序,该处理程序在提交 HTML 表单且请求具有有效的 CSRF 令牌时执行,如下所示:package main
import
(
"fmt"
"html/template"
"log"
"net/http"
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
)
const
(
CONN_HOST = "localhost"
CONN_PORT = "8443"
HTTPS_CERTIFICATE = "domain.crt"
DOMAIN_PRIVATE_KEY = "domain.key"
)
var AUTH_KEY = []byte("authentication-key")
func signUp(w http.ResponseWriter, r *http.Request)
{
parsedTemplate, _ := template.ParseFiles("sign-up.html")
err := parsedTemplate.Execute
(
w, map[string]interface{}
{
csrf.TemplateTag: csrf.TemplateField(r),
}
)
if err != nil
{
log.Printf("Error occurred while executing the
template : ", err)
return
}
}
func post(w http.ResponseWriter, r *http.Request)
{
err := r.ParseForm()
if err != nil
{
log.Print("error occurred while parsing form ", err)
}
name := r.FormValue("name")
fmt.Fprintf(w, "Hi %s", name)
}
func main()
{
muxRouter := mux.NewRouter().StrictSlash(true)
muxRouter.HandleFunc("/signup", signUp)
muxRouter.HandleFunc("/post", post)
http.ListenAndServeTLS(CONN_HOST+":"+CONN_PORT,
HTTPS_CERTIFICATE, DOMAIN_PRIVATE_KEY, csrf.Protect
(AUTH_KEY)(muxRouter))
}
$ go run prevent-csrf.go
一旦我们运行程序,HTTP 服务器将在端口8443
上开始本地侦听。
接下来,从命令行执行一个POST
请求,如下所示:
$ curl -X POST --data "name=Foo&email=aggarwalarpit.89@gmail.com" https://localhost:8443/post --insecure
它将向您发送禁止-CSRF 令牌无效消息作为服务器的响应,并禁止您提交 HTML 表单,因为服务器未在请求中找到有效的 CSRF 令牌:
因此,要提交表单,首先我们必须注册,它通过执行以下命令生成有效的 CSRF 令牌:
$ curl -i -X GET https://localhost:8443/signup --insecure
这将为您提供一个 HTTPX-CSRF-Token
,如以下屏幕截图所示:
现在您必须将其作为 HTTPX-CSRF-Token
请求头和 HTTP cookie 一起传递,以提交 HTML 表单,如下所示:
$ curl -X POST --data "name=Foo&email=aggarwalarpit.89@gmail.com" -H "X-CSRF-Token: M9gqV7rRcXERvSJVRSYprcMzwtFmjEHKXRm6C8cDC4EjTLIt4OiNzVrHfYNB12nEx280rrKs8fqOgvfcJgQiFA==" --cookie "_gorilla_csrf=MTUyMzQzMjg0OXxJa1ZLVTFsbGJHODFMMHg0VEdWc0wxZENVRVpCWVZGU1l6bHVMMVZKVEVGM01EVjBUakVyUlVoTFdsVTlJZ289fJI5dumuyObaHVp97GN_CiZBCCpnbO0wlIwgSgvHL7-C;" https://localhost:8443/post --insecure
Hi Foo
让我们了解一下我们编写的程序:
const (CONN_HOST = "localhost" CONN_PORT = "8443" HTTPS_CERTIFICATE = "domain.crt" DOMAIN_PRIVATE_KEY = "domain.key")
:在这里,我们声明了四个常量-CONN_HOST
的值为localhost
、CONN_PORT
的值为8443
、HTTPS_CERTIFICATE
的值为domain.crt
或自签名证书、DOMAIN_PRIVATE_KEY
的值为domain.key
或我们在之前配方中创建的私钥。var AUTH_KEY = []byte("authentication-key")
:用于生成 CSRF 令牌的认证密钥。signUp
:这是一个解析sign-up.html
并提供一个<input>
字段的处理程序,该字段填充了一个 CSRF 令牌来替换表单中的{{ .csrfField }}
。post
:这是一个处理程序,它解析提交的表单,获取名称输入字段的值,并将其写入 HTTP 响应流。最后,我们定义了main()
,其中我们使用NewRouter()
处理程序创建了一个gorilla/mux
路由器实例,新路由的尾部斜杠行为为true
,将/signup
路由注册为signUp
处理程序,将/post
路由注册为post
处理程序,并将http.ListenAndServeTLS
传递处理程序称为csrf.Protect(AUTH_KEY)(muxRouter)
,确保所有没有有效令牌的POST
请求将返回HTTP 403 Forbidden
。