JWT认证 #
一、JWT概述 #
1.1 JWT结构 #
JWT由三部分组成:Header.Payload.Signature
text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuW8oOS4iSIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
1.2 JWT流程 #
text
1. 用户登录 → 服务器验证
2. 验证成功 → 生成JWT
3. 返回JWT → 客户端存储
4. 后续请求 → 携带JWT
5. 服务器验证JWT → 返回数据
二、安装JWT库 #
bash
go get github.com/golang-jwt/jwt/v5
三、Token生成 #
3.1 定义Claims #
go
type JwtClaims struct {
UserID uint `json:"user_id"`
Name string `json:"name"`
Role string `json:"role"`
jwt.RegisteredClaims
}
3.2 生成Token #
go
var jwtSecret = []byte("your-secret-key")
func GenerateToken(userID uint, name, role string) (string, error) {
claims := JwtClaims{
UserID: userID,
Name: name,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "myapp",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}
3.3 登录接口 #
go
type LoginRequest struct {
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
}
e.POST("/login", func(c echo.Context) error {
req := new(LoginRequest)
if err := c.Bind(req); err != nil {
return err
}
user, err := authenticate(req.Username, req.Password)
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "用户名或密码错误")
}
token, err := GenerateToken(user.ID, user.Name, user.Role)
if err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{
"token": token,
})
})
四、Token验证 #
4.1 解析Token #
go
func ParseToken(tokenString string) (*JwtClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JwtClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
4.2 JWT中间件 #
go
func JWTMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
auth := c.Request().Header.Get("Authorization")
if auth == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
}
parts := strings.Split(auth, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
return echo.NewHTTPError(http.StatusUnauthorized, "Token格式错误")
}
claims, err := ParseToken(parts[1])
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Token无效")
}
c.Set("userID", claims.UserID)
c.Set("name", claims.Name)
c.Set("role", claims.Role)
return next(c)
}
}
4.3 使用中间件 #
go
api := e.Group("/api")
api.Use(JWTMiddleware)
api.GET("/profile", getProfile)
api.GET("/users", getUsers)
五、内置JWT中间件 #
5.1 配置JWT中间件 #
go
e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
SigningKey: []byte("secret"),
TokenLookup: "header:Authorization:Bearer ",
AuthScheme: "Bearer",
ContextKey: "user",
}))
5.2 获取用户信息 #
go
e.GET("/profile", func(c echo.Context) error {
user := c.Get("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
return c.JSON(http.StatusOK, map[string]interface{}{
"user_id": claims["user_id"],
"name": claims["name"],
"role": claims["role"],
})
})
六、Token刷新 #
6.1 刷新机制 #
go
func GenerateRefreshToken(userID uint) (string, error) {
claims := jwt.RegisteredClaims{
Subject: fmt.Sprintf("%d", userID),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}
func RefreshToken(refreshToken string) (string, error) {
token, err := jwt.ParseWithClaims(refreshToken, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if err != nil || !token.Valid {
return "", errors.New("invalid refresh token")
}
claims := token.Claims.(*jwt.RegisteredClaims)
userID, _ := strconv.ParseUint(claims.Subject, 10, 64)
user, err := getUserByID(uint(userID))
if err != nil {
return "", err
}
return GenerateToken(user.ID, user.Name, user.Role)
}
6.2 刷新接口 #
go
e.POST("/refresh", func(c echo.Context) error {
refreshToken := c.FormValue("refresh_token")
if refreshToken == "" {
return echo.NewHTTPError(http.StatusBadRequest, "缺少refresh token")
}
token, err := RefreshToken(refreshToken)
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "refresh token无效")
}
return c.JSON(http.StatusOK, map[string]string{
"token": token,
})
})
七、权限控制 #
7.1 角色中间件 #
go
func RoleMiddleware(roles ...string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
role := c.Get("role").(string)
for _, r := range roles {
if role == r {
return next(c)
}
}
return echo.NewHTTPError(http.StatusForbidden, "权限不足")
}
}
}
7.2 使用角色中间件 #
go
admin := e.Group("/admin")
admin.Use(JWTMiddleware)
admin.Use(RoleMiddleware("admin"))
admin.GET("/users", listUsers)
admin.DELETE("/users/:id", deleteUser)
7.3 权限验证 #
go
func checkPermission(c echo.Context, resource, action string) bool {
role := c.Get("role").(string)
permissions := map[string]map[string][]string{
"admin": {
"users": {"read", "write", "delete"},
"posts": {"read", "write", "delete"},
},
"editor": {
"posts": {"read", "write"},
},
"user": {
"posts": {"read"},
},
}
if actions, ok := permissions[role][resource]; ok {
for _, a := range actions {
if a == action {
return true
}
}
}
return false
}
func PermissionMiddleware(resource, action string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if !checkPermission(c, resource, action) {
return echo.NewHTTPError(http.StatusForbidden, "权限不足")
}
return next(c)
}
}
}
八、完整示例 #
go
package main
import (
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
type User struct {
ID uint `json:"id"`
Username string `json:"username"`
Password string `json:"-"`
Name string `json:"name"`
Role string `json:"role"`
}
type JwtClaims struct {
UserID uint `json:"user_id"`
Name string `json:"name"`
Role string `json:"role"`
jwt.RegisteredClaims
}
var jwtSecret = []byte("your-secret-key")
var users = map[string]*User{
"admin": {ID: 1, Username: "admin", Password: "admin123", Name: "管理员", Role: "admin"},
"user": {ID: 2, Username: "user", Password: "user123", Name: "普通用户", Role: "user"},
}
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.POST("/login", login)
e.POST("/refresh", refreshToken)
api := e.Group("/api")
api.Use(JWTMiddleware)
api.GET("/profile", getProfile)
admin := api.Group("/admin")
admin.Use(RoleMiddleware("admin"))
admin.GET("/users", listUsers)
e.Logger.Fatal(e.Start(":8080"))
}
func GenerateToken(userID uint, name, role string) (string, error) {
claims := JwtClaims{
UserID: userID,
Name: name,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "myapp",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}
func ParseToken(tokenString string) (*JwtClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return jwtSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JwtClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
func JWTMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
auth := c.Request().Header.Get("Authorization")
if auth == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
}
parts := strings.Split(auth, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
return echo.NewHTTPError(http.StatusUnauthorized, "Token格式错误")
}
claims, err := ParseToken(parts[1])
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Token无效或已过期")
}
c.Set("userID", claims.UserID)
c.Set("name", claims.Name)
c.Set("role", claims.Role)
return next(c)
}
}
func RoleMiddleware(roles ...string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
role := c.Get("role").(string)
for _, r := range roles {
if role == r {
return next(c)
}
}
return echo.NewHTTPError(http.StatusForbidden, "权限不足")
}
}
}
func login(c echo.Context) error {
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
req := new(LoginRequest)
if err := c.Bind(req); err != nil {
return err
}
user, ok := users[req.Username]
if !ok || user.Password != req.Password {
return echo.NewHTTPError(http.StatusUnauthorized, "用户名或密码错误")
}
token, err := GenerateToken(user.ID, user.Name, user.Role)
if err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{
"token": token,
})
}
func refreshToken(c echo.Context) error {
return echo.NewHTTPError(http.StatusNotImplemented, "功能待实现")
}
func getProfile(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]interface{}{
"user_id": c.Get("userID"),
"name": c.Get("name"),
"role": c.Get("role"),
})
}
func listUsers(c echo.Context) error {
return c.JSON(http.StatusOK, users)
}
九、总结 #
JWT认证要点:
| 要点 | 说明 |
|---|---|
| Token生成 | jwt.NewWithClaims() |
| Token签名 | token.SignedString() |
| Token解析 | jwt.ParseWithClaims() |
| JWT中间件 | 验证Token |
| 角色控制 | RoleMiddleware |
| 权限验证 | PermissionMiddleware |
准备好学习文件上传下载了吗?让我们进入下一章!
最后更新:2026-03-28