之前写的《golang实现一个简单的HTTP server》没有包含认证部分
本例给出了支持BasicAuth的实现,以及如何在一个项目中导入自己定义的package
编写代码
- 创建项目所在文件夹
admin@hpc-1:~/go$ mkdir auth_http
admin@hpc-1:~/go$ cd auth_http
admin@hpc-1:~/go/auth_http$
- 项目初始化为一个模块,多出了一个go.mod文件
admin@hpc-1:~/go/auth_http$ go mod init my_auth
go: creating new go.mod: module my_auth
admin@hpc-1:~/go/auth_http$ admin@hpc-1:~/go/auth_http$ cat go.mod
module my_authgo 1.18
admin@hpc-1:~/go/auth_http$
- 补齐代码后的目录结构
admin@hpc-1:~/go/auth_http$ tree
.
├── go.mod
├── http_rpc_server.go
└── utils└── do_auth.go1 directory, 3 files
admin@hpc-1:~/go/auth_http$
- 代码内容
admin@hpc-1:~/go/auth_http$ cat http_rpc_server.go
package mainimport ("fmt""log""flag""net/http""encoding/json""my_auth/utils"
)// 定义一个用于接收请求的结构体
type MapPrinter struct{}// 定义一个用于接收请求的方法
func (m *MapPrinter) PrintMap(w http.ResponseWriter, r *http.Request) {if r.Method != http.MethodPost {http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)return}// 检查认证头部信息username, password, ok := r.BasicAuth()if !ok {// 未提供基本身份验证,返回 401 Unauthorizedw.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)w.WriteHeader(http.StatusUnauthorized)fmt.Fprintln(w, "401 Unauthorized")return}// 验证用户名和密码if !utils.CheckCredentials(username, password) {// 用户名和密码不匹配,返回 401 Unauthorizedw.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)w.WriteHeader(http.StatusUnauthorized)fmt.Fprintln(w, "401 Unauthorized")return}var payload map[string]interface{}err := json.NewDecoder(r.Body).Decode(&payload)if err != nil {http.Error(w, "Invalid payload", http.StatusBadRequest)return}fmt.Println("Received payload:")for key, value := range payload {fmt.Printf("%s: %v\n", key, value)}w.WriteHeader(http.StatusOK)w.Write([]byte("Map printed successfully\n"))
}func main() {//获取监听端口,默认8080port := flag.String("p", "8080", "指定监听端口")// 解析命令行参数flag.Parse()//注册abc-api路由http.HandleFunc("/abc-api", new(MapPrinter).PrintMap)admin@hpc-1:~/go/auth_http$
admin@hpc-1:~/go/auth_http$
admin@hpc-1:~/go/auth_http$
admin@hpc-1:~/go/auth_http$ cat utils/do_auth.go
package utilsfunc CheckCredentials(username, password string) bool {validUsername := "admin"validPassword := "admin"return username == validUsername && password == validPassword
}
admin@hpc-1:~/go/auth_http$
一些说明
go mode init <package_name>
是将代码从GOPATH 模式迁移到模块模式。然后使用模块路径来导入其他模块或包- golang的项目必须要有
- 至少一个go文件以
package main
开头 - 有且仅有一个go文件包含
func main()
作为程序执行的入口
- 至少一个go文件以
utils
目录下的所有文件(本例中只有do_auth.go一个文件)都以package utils
开头,表明这些文件都属于utils
这个自定义包的组成部分- 注意,utils中的函数如何要被外部函数调用,必须
以大写字母开头
,例如本例中的CheckCredentials - 当要调用
utils
中的函数的时候import "my_auth/utils"
,就是由init时候创建的模块加上"/",再加上自定义的package name- 调用
utils
里面的函数的时候,使用utils.Xxxxx来调用,此时和那个函数具体写在哪个文件中就没有关系了(本例中调用时候就不会出现do_auth这个文件名)
运行
- 编译出可执行文件
admin@hpc-1:~/go/auth_http$ go build -o http_rpc_server
admin@hpc-1:~/go/auth_http$
admin@hpc-1:~/go/auth_http$ file http_rpc_server
http_rpc_server: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=jj_W4SsJdLxNOWHH706-/p8kU3nvIDJ1cRjEIGWFi/752lBQWnErUy3P6gdHgN/2ksEePP3sNj-He57uoCu, not stripped
admin@hpc-1:~/go/auth_http$
- 运行
admin@hpc-1:~/go/auth_http$ chmod +x http_rpc_server
admin@hpc-1:~/go/auth_http$
admin@hpc-1:~/go/auth_http$ ./http_rpc_server -p 8090
Server is listening on port %s... 8090
验证
不指定auth header
- 另开终端,用curl发送POST请求,不带username-password
admin@hpc-1:~/go/auth_http$ curl -X POST -H "Content-Type: application/json" -d '{"key1":"value1", "key2":"value2"}' http://localhost:8090/abc-api
401 Unauthorized
admin@hpc-1:~/go/auth_http$
auth header与期望的不匹配
- 代码中期望用户名和密码为admin/admin,故意不匹配
admin@hpc-1:~/go/auth_http$ curl -X POST -u admin:password -H "Content-Type: application/json" -d '{"key1":"value1", "key2":"value2"}' http://localhost:8090/abc-api
401 Unauthorized
admin@hpc-1:~/go/auth_http$
auth header与期望的匹配
- 匹配期望的用户名密码
admin@hpc-1:~/go/auth_http$ curl -X POST -u admin:admin -H "Content-Type: application/json" -d '{"key1":"value1", "key2":"value2"}' http://localhost:8090/abc-api
Map printed successfully
admin@hpc-1:~/go
- 启动server的终端打印
admin@hpc-1:~/go/auth_http$ ./http_rpc_server -p 8090
Server is listening on port %s... 8090
Received payload:
key1: value1
key2: value2