API接口认证是验证请求方身份合法性的过程,在Golang开发中,我们可以根据业务场景选择不同的认证方案,常见的包括Token认证、签名认证、OAuth2授权等,不同的方案适配不同的使用场景。

Golang实现API认证的常见方案
1. 基于JWT的Token认证
JWT是目前前后端分离场景下最常用的认证方式,Golang中可以使用github.com/golang-jwt/jwt/v5库实现JWT的生成和校验。首先需要定义JWT的声明结构,然后实现生成Token和解析Token的方法。
生成Token的示例代码如下:
package main
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
// 定义JWT声明结构
type Claims struct {
UserID uint64 `json:"user_id"`
jwt.RegisteredClaims
}
// 生成JWT Token
func GenerateToken(userID uint64, secret string) (string, error) {
// 设置声明信息
claims := Claims{
UserID: userID,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 过期时间24小时
IssuedAt: jwt.NewNumericDate(time.Now()), // 签发时间
Issuer: "api-service", // 签发者
},
}
// 使用HS256算法生成Token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
解析校验Token的示例代码如下:
// 解析校验JWT Token
func ParseToken(tokenStr string, secret string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
// 校验签名算法是否为HS256
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(secret), nil
})
if err != nil {
return nil, err
}
// 校验Token是否有效,并提取声明
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, fmt.Errorf("invalid token")
}
2. 接口签名认证
签名认证适合服务端之间的调用场景,通过请求参数加签、验签的方式验证请求合法性,避免请求被篡改。实现逻辑是请求方按照约定规则拼接参数、生成签名,接收方按照相同规则校验签名是否一致。
签名生成和校验的示例代码如下:
package main
import (
"crypto/md5"
"encoding/hex"
"fmt"
"net/url"
"sort"
)
// 生成请求签名
func GenerateSign(params url.Values, secret string) string {
// 提取所有参数键并排序
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// 拼接参数字符串
var paramStr string
for _, k := range keys {
paramStr += fmt.Sprintf("%s=%s&", k, params.Get(k))
}
// 拼接密钥并计算MD5
paramStr += "secret=" + secret
hash := md5.Sum([]byte(paramStr))
return hex.EncodeToString(hash[:])
}
// 校验请求签名
func VerifySign(params url.Values, secret string, clientSign string) bool {
serverSign := GenerateSign(params, secret)
return serverSign == clientSign
}
认证与授权的区分
很多开发者会混淆认证和授权的概念,两者有本质区别:
- 认证:验证请求方是谁,确认身份合法性,比如上述的JWT校验、签名校验都属于认证环节
- 授权:验证已认证的请求方是否有权限执行某个操作,比如判断用户是否有删除数据的权限,属于授权环节
在Golang中,授权通常可以在认证中间件之后,通过读取认证信息中的用户角色、权限列表,结合业务逻辑判断实现。
认证实现的安全注意事项
生产环境中实现API认证时,需要注意以下安全细节,避免引入漏洞
- JWT的密钥需要妥善保管,不要硬编码在代码中,建议使用环境变量或配置中心存储
- Token的过期时间需要根据业务场景合理设置,避免过长导致安全风险
- 签名认证中建议使用更安全的哈希算法,比如SHA256,避免使用MD5等已被证明不安全的算法
- 所有涉及认证的接口都需要使用HTTPS传输,避免请求被抓包窃取敏感信息
- 可以在认证中间件中添加请求频率限制,避免暴力破解Token或签名
认证中间件封装示例
可以将JWT认证逻辑封装成Golang的HTTP中间件,方便在所有需要认证的接口中复用:
package main
import (
"net/http"
"strings"
)
// JWT认证中间件
func JWTAuthMiddleware(secret string) func(http.HandlerFunc) http.HandlerFunc {
return func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 从请求头获取Authorization
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "missing authorization header", http.StatusUnauthorized)
return
}
// 提取Token,格式为Bearer xxx
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
http.Error(w, "invalid authorization format", http.StatusUnauthorized)
return
}
tokenStr := parts[1]
// 校验Token
claims, err := ParseToken(tokenStr, secret)
if err != nil {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
// 将用户信息存入请求上下文,方便后续处理使用
ctx := context.WithValue(r.Context(), "user_id", claims.UserID)
next.ServeHTTP(w, r.WithContext(ctx))
}
}
}
使用时只需要在路由注册时添加中间件即可,比如r.HandleFunc("/api/user/info", JWTAuthMiddleware(secret)(getUserInfoHandler)),就可以实现该接口的认证保护。