Golang中如何理解并使用JWT?
Golang中如何理解并使用JWT? 我不理解JWT。 我从网上了解到一些信息:
- 它提供安全的API调用
- 确保数据在传输过程中未被篡改
- 对API调用进行身份验证
但我无法理解的是:
- 如何让用户知道他的令牌
- 如何携带令牌向服务器发送请求(POST/GET或其他方式)
- 是否有其他替代方案?
你可能会发现我关于JWT和OpenID Connect的文章很有帮助。
更多关于Golang中如何理解并使用JWT?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
AnikHasibul:
如何让用户知道他的令牌。
JWT 本身并未对此作出规定。它只规定了令牌的格式、包含的内容以及加密或签名的方式。
我构建的一个应用程序采用以下步骤:
- 用户导航到登录页面
- 输入用户名和密码,提交登录表单
- 服务器验证用户名和密码
- 验证通过后,服务器为用户创建 JWT
- 服务器在响应中设置 cookie
- 用户浏览器存储该 cookie,并在后续所有请求中自动发送给服务器
- 服务器从浏览器发送的 cookie 中提取 JWT
- 服务器验证 JWT(签名有效、未过期)以确认用户身份认证成功
- 必要时,服务器会检查 JWT 中的附加声明,以决定是否允许用户访问所请求的资源
我也将 JWT 用于服务端之间的通信。接收服务器的处理流程与上述类似。但请求服务器无法执行登录操作,因此我会生成一个长期有效的 JWT 并将其存储在请求服务器的配置中。
如何携带令牌向服务器发送请求(POST/GET 或其他方式)
有两种方案:
- 使用 HTTP 头部
Authorization: Bearer <token> - 使用 HTTP cookie(同样通过 HTTP 头部发送)
具体从哪个 HTTP 头部获取 JWT 由服务器端决定。
是否有其他替代方案?
据我所知没有。JWT 是一项优秀的技术,具有易于理解、可扩展性强、支持多种编程语言、在 Web 应用中便于使用等特点。
在Go语言中,JWT(JSON Web Token)是一种用于安全传输信息的开放标准,常用于API身份验证和授权。它由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。下面我将解释JWT的基本概念,并提供Go代码示例来演示如何生成、解析和携带JWT令牌。
JWT的基本理解
- JWT通过数字签名确保数据的完整性和真实性,防止篡改。它不加密数据本身(除非使用JWE),因此不要在载荷中存储敏感信息。
- 在身份验证流程中,服务器生成JWT并发送给客户端(例如,通过登录响应)。客户端在后续请求中携带此令牌(通常在HTTP头中),服务器验证令牌以确认用户身份。
如何让用户知道他的令牌
在典型的Web应用中,用户通过登录端点(如/login)提交凭据(如用户名和密码)。服务器验证凭据后,生成JWT并返回给客户端。客户端可以将其存储在本地(如localStorage、cookie或内存中),以便后续使用。例如,在登录响应中返回JSON:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsImV4cCI6MTYzMDAwMDAwMH0.abc123..."
}
如何携带令牌向服务器发送请求
客户端在请求中携带JWT,通常通过HTTP头的Authorization字段,使用Bearer方案。例如:
Authorization: Bearer <token>
在Go中,你可以使用标准库或第三方包(如github.com/golang-jwt/jwt)来处理JWT。以下是一个完整示例,展示生成令牌、验证令牌和在HTTP请求中使用的过程。
示例代码:生成和验证JWT
首先,安装JWT包:
go get github.com/golang-jwt/jwt
然后,编写Go代码:
package main
import (
"fmt"
"net/http"
"time"
"github.com/golang-jwt/jwt"
)
// 定义一个密钥,用于签名和验证JWT。在生产环境中,使用安全的方式存储。
var jwtKey = []byte("my_secret_key")
// 定义JWT的载荷结构
type Claims struct {
UserID int `json:"user_id"`
jwt.StandardClaims
}
// 生成JWT令牌的函数
func generateToken(userID int) (string, error) {
expirationTime := time.Now().Add(24 * time.Hour) // 令牌过期时间为24小时
claims := &Claims{
UserID: userID,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
Issuer: "test_app",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtKey)
}
// 验证JWT令牌的函数
func verifyToken(tokenStr string) (*Claims, error) {
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
return claims, nil
}
// 登录处理函数:模拟用户登录并返回JWT
func loginHandler(w http.ResponseWriter, r *http.Request) {
// 假设用户验证成功,用户ID为123
userID := 123
token, err := generateToken(userID)
if err != nil {
http.Error(w, "Failed to generate token", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"token": "%s"}`, token)
}
// 受保护API的处理函数:验证JWT并返回数据
func protectedHandler(w http.ResponseWriter, r *http.Request) {
// 从Authorization头中提取令牌
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Authorization header missing", http.StatusUnauthorized)
return
}
// 期望格式:Bearer <token>
if len(authHeader) < 7 || authHeader[:7] != "Bearer " {
http.Error(w, "Invalid authorization format", http.StatusUnauthorized)
return
}
tokenStr := authHeader[7:]
claims, err := verifyToken(tokenStr)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Access granted for user ID: %d", claims.UserID)
}
func main() {
http.HandleFunc("/login", loginHandler)
http.HandleFunc("/protected", protectedHandler)
http.ListenAndServe(":8080", nil)
}
在这个示例中:
generateToken函数基于用户ID生成JWT,使用HS256算法签名。verifyToken函数解析并验证JWT,如果有效则返回载荷。loginHandler处理登录请求,返回JWT令牌。protectedHandler是一个受保护的端点,它检查请求头中的JWT,验证通过后才允许访问。
客户端在登录后获取令牌,然后在请求受保护资源时,在HTTP头中添加Authorization: Bearer <token>。你可以使用工具如curl测试:
# 登录获取令牌
curl -X POST http://localhost:8080/login
# 使用令牌访问受保护端点(替换<token>为实际令牌)
curl -H "Authorization: Bearer <token>" http://localhost:8080/protected
是否有其他替代方案?
是的,JWT有多种替代方案,具体选择取决于应用场景:
- Session-Based Authentication:使用服务器端存储的会话ID(例如,通过cookie)。优点是服务器可以主动撤销会话,但需要状态存储。
- OAuth 2.0 / OpenID Connect:适用于第三方授权和单点登录(SSO),更复杂但功能丰富。
- API Keys:简单但安全性较低,适用于内部或低风险场景。
- SAML:基于XML的标准,常用于企业级SSO。
在Go中,你可以根据需求选择这些方案。例如,对于会话认证,可以使用gorilla/sessions包;对于OAuth,可以使用golang.org/x/oauth2包。
总之,JWT在无状态API中非常高效,但需注意令牌过期和撤销机制。通过以上示例,你可以开始在Go项目中实现JWT身份验证。

