业务环境回放Go认证授权与权限测试 - 第3节
3.1 HTTP 协议核心:请求与响应结构
HTTP(HyperText Transfer Protocol)是 Web 服务与 RESTful API 的底层通信协议。一个完整的 HTTP 交互由客户端请求和服务端响应构成,两者均遵循固定的报文格式。
3.1.1 HTTP 请求结构
一个典型的 HTTP 请求包含以下四部分:
- 请求行(Request Line):包含 HTTP 方法、请求 URI 和协议版本。例如
GET /api/users HTTP/1.1。 - 请求头(Headers):键值对形式,传递元数据,如
Content-Type、Authorization、User-Agent。 - 空行(CRLF):分隔头部与消息体。
- 消息体(Body):可选,用于 POST、PUT 等方法传输数据。
关键点:在 Go 的
net/http 中,http.Request 结构体直接映射了上述四个部分,开发者通过 r.Method、r.URL、r.Header、r.Body 访问。
3.1.2 HTTP 响应结构
服务端返回的响应同样由四部分组成:
- 状态行(Status Line):协议版本、状态码和原因短语,如
HTTP/1.1 200 OK。 - 响应头(Headers):如
Content-Type、Set-Cookie。 - 空行:分隔头部与消息体。
- 消息体(Body):返回的资源内容,通常是 JSON、HTML 或纯文本。
| 状态码范围 | 类别 | 常见示例 |
|---|---|---|
| 1xx | 信息性 | 100 Continue |
| 2xx | 成功 | 200 OK, 201 Created |
| 3xx | 重定向 | 301 Moved Permanently, 304 Not Modified |
| 4xx | 客户端错误 | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found |
| 5xx | 服务端错误 | 500 Internal Server Error, 502 Bad Gateway |
权限验证提示:401 与 403 是权限体系中最常用的状态码。401 表示“未认证”,需要提供凭证;403 表示“已认证但无权限”,禁止访问资源。
3.1.3 HTTP 请求/响应交互流程
下图展示了客户端与服务端之间一次完整的 HTTP 请求-响应循环,以及 Go 标准库中对应的处理入口。
3.2 RESTful API 设计原则
REST(Representational State Transfer)是一种基于 HTTP 协议的软件架构风格,强调资源导向、无状态和统一接口。在 Go 权限验证系统中,遵循 RESTful 原则可以使 API 更清晰、可维护性更高。
3.2.1 核心原则
| 原则 | 说明 | 权限验证示例 |
|---|---|---|
| 资源导向 | 使用名词表示资源,而非动词 | /api/users 而非 /api/getUsers |
| 统一接口 | 通过 HTTP 方法操作资源 | GET 读取、POST 创建、PUT 更新、DELETE 删除 |
| 无状态 | 每个请求包含所有必要信息,服务端不保存客户端上下文 | JWT Token 随请求发送,服务端验证 |
| 可缓存 | 利用 HTTP 缓存机制提升性能 | 权限列表可设置 Cache-Control |
| 分层系统 | 客户端无需知道直接与哪一层交互 | API 网关统一鉴权,后端服务专注业务 |
3.2.2 资源命名与 HTTP 方法映射
在 Go 权限验证系统中,常见的资源与操作映射如下:
- GET /api/users — 获取用户列表(需认证)
- GET /api/users/{id} — 获取单个用户详情(需认证 + 本人或管理员)
- POST /api/users — 创建用户(需管理员权限)
- PUT /api/users/{id} — 更新用户信息(需认证 + 本人或管理员)
- DELETE /api/users/{id} — 删除用户(需管理员权限)
- GET /api/roles — 获取角色列表(需认证)
- POST /api/roles/{id}/permissions — 为角色分配权限(需管理员权限)
常见误区:不要将动词放入 URL,如
/api/deleteUser 或 /api/createRole。应使用 HTTP 方法表达操作意图,URL 只表示资源。
3.3 Go 标准库 net/http 基础
Go 的 net/http 标准库提供了构建 HTTP 客户端和服务器的完整能力,无需第三方依赖即可实现 RESTful API。
3.3.1 快速启动一个 HTTP 服务器
以下代码创建一个监听 8080 端口的服务器,并注册两个路由:
package main
import (
"fmt"
"net/http"
)
func main() {
// 注册路由
http.HandleFunc("/api/health", healthHandler)
http.HandleFunc("/api/users", usersHandler)
// 启动服务器
fmt.Println("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"status":"ok"}`)
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
// 根据 HTTP 方法分发
switch r.Method {
case http.MethodGet:
// 处理 GET 请求
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `[{"id":1,"name":"Alice"}]`)
case http.MethodPost:
// 处理 POST 请求
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{"id":2,"name":"Bob"}`)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, `{"error":"method not allowed"}`)
}
}
3.3.2 核心类型与接口
| 类型/接口 | 作用 | 权限验证中的用途 |
|---|---|---|
http.Handler |
接口,包含 ServeHTTP(ResponseWriter, *Request) 方法 |
实现自定义中间件,如权限验证中间件 |
http.HandlerFunc |
函数适配器,将普通函数转为 Handler | 快速定义路由处理函数 |
http.ServeMux |
默认的路由多路复用器 | 注册不同路径的处理函数 |
http.ResponseWriter |
用于构建 HTTP 响应 | 写入状态码、响应头、消息体 |
http.Request |
表示客户端请求 | 提取 Token、路径参数、查询参数 |
3.3.3 中间件模式与权限验证
Go 的 net/http 通过 Handler 嵌套实现中间件模式,这是权限验证的核心机制。下图展示了请求经过中间件链到达业务处理器的流程。
3.3.4 实现一个简单的认证中间件
// 认证中间件:验证请求头中的 Bearer Token
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 从 Authorization 头提取 Token
authHeader := r.Header.Get("Authorization")
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, `{"error":"missing or invalid token"}`)
return
}
token := strings.TrimPrefix(authHeader, "Bearer ")
// 验证 Token(此处为示例,实际应解析 JWT)
if token != "valid-token" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, `{"error":"invalid token"}`)
return
}
// 将用户信息注入上下文(后续章节详解)
// ctx := context.WithValue(r.Context(), "user", "admin")
// r = r.WithContext(ctx)
// 调用下一个 Handler
next.ServeHTTP(w, r)
})
}
// 使用中间件保护路由
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/public", publicHandler)
mux.Handle("/api/protected", authMiddleware(http.HandlerFunc(protectedHandler)))
http.ListenAndServe(":8080", mux)
}
实战要点:在 Go 的权限验证中,
http.Handler 的嵌套是构建中间件链的基础。认证中间件负责验证身份,授权中间件负责检查权限,两者职责分离,便于测试和复用。