JWT Starter

简介

JWT(JSON Web Token)Starter 为 Hiboot 应用程序提供身份验证和授权功能。它支持令牌生成、验证和中间件集成。

安装

在应用程序中导入 JWT starter:

import "github.com/hidevopsio/hiboot/pkg/starter/jwt"

配置

application.yml 中配置 JWT:

app:
  profiles:
    include:
    - jwt

jwt:
  # RSA 私钥文件路径
  private_key_path: "config/keys/private.pem"
  # RSA 公钥文件路径
  public_key_path: "config/keys/public.pem"

生成 RSA 密钥

生成用于令牌签名的 RSA 密钥对:

# 生成私钥
openssl genrsa -out config/keys/private.pem 2048

# 从私钥生成公钥
openssl rsa -in config/keys/private.pem -pubout -out config/keys/public.pem

基本用法

生成令牌

注入 jwt.Token 来生成令牌:

package controller

import (
	"time"

	"github.com/hidevopsio/hiboot/pkg/app"
	"github.com/hidevopsio/hiboot/pkg/at"
	"github.com/hidevopsio/hiboot/pkg/model"
	"github.com/hidevopsio/hiboot/pkg/starter/jwt"
)

type loginController struct {
	at.RestController
	at.RequestMapping `value:"/auth"`

	token jwt.Token
}

type LoginRequest struct {
	model.RequestBody
	Username string `json:"username" validate:"required"`
	Password string `json:"password" validate:"required"`
}

type LoginResponse struct {
	Token     string `json:"token"`
	ExpiresIn int64  `json:"expires_in"`
}

func init() {
	app.Register(newLoginController)
}

func newLoginController(token jwt.Token) *loginController {
	return &loginController{
		token: token,
	}
}

// Post 处理 POST /auth/login
func (c *loginController) PostLogin(request *LoginRequest) (model.Response, error) {
	// 验证凭据(实现你自己的逻辑)
	if request.Username != "admin" || request.Password != "password" {
		response := new(model.BaseResponse)
		response.SetCode(401)
		response.SetMessage("凭据无效")
		return response, nil
	}

	// 生成 JWT 令牌
	claims := jwt.Map{
		"username": request.Username,
		"role":     "admin",
	}

	// 令牌在 30 分钟后过期
	tokenString, err := c.token.Generate(claims, 30, time.Minute)
	if err != nil {
		return nil, err
	}

	response := new(model.BaseResponse)
	response.SetData(&LoginResponse{
		Token:     tokenString,
		ExpiresIn: 1800, // 30 分钟(秒)
	})
	return response, nil
}

JWT 中间件

使用 JWT 中间件保护路由:

package controller

import (
	"github.com/hidevopsio/hiboot/pkg/app"
	"github.com/hidevopsio/hiboot/pkg/at"
	"github.com/hidevopsio/hiboot/pkg/model"
	"github.com/hidevopsio/hiboot/pkg/starter/jwt"
)

type protectedController struct {
	at.RestController
	at.RequestMapping `value:"/api"`

	// 用于路由保护的 JWT 中间件
	Middleware *jwt.Middleware `inject:""`
}

func init() {
	app.Register(newProtectedController)
}

func newProtectedController() *protectedController {
	return &protectedController{}
}

// Before 在每个请求之前调用
// 使用此方法应用 JWT 验证
func (c *protectedController) Before() {
	c.Middleware.Serve(c.Ctx)
}

// GetProfile 处理 GET /api/profile
func (c *protectedController) GetProfile() (model.Response, error) {
	// 从上下文获取声明
	claims := c.Ctx.Values().Get("jwt").(*jwt.Token)

	response := new(model.BaseResponse)
	response.SetData(map[string]interface{}{
		"username": claims.Get("username"),
		"role":     claims.Get("role"),
	})
	return response, nil
}

令牌 API

jwt.Token 接口

type Token interface {
	// Generate 创建新的 JWT 令牌
	Generate(claims Map, expiresAt int64, timeUnit time.Duration) (string, error)

	// VerifyKey 返回用于验证的公钥
	VerifyKey() interface{}

	// SignKey 返回用于签名的私钥
	SignKey() interface{}
}

jwt.Map

jwt.Mapmap[string]interface{} 的别名,用于令牌声明:

claims := jwt.Map{
	"user_id":  123,
	"username": "john",
	"role":     "admin",
	"permissions": []string{"read", "write"},
}

高级用法

自定义令牌过期时间

// 令牌有效期 24 小时
token, err := c.token.Generate(claims, 24, time.Hour)

// 令牌有效期 7 天
token, err := c.token.Generate(claims, 7*24, time.Hour)

// 令牌有效期 15 分钟
token, err := c.token.Generate(claims, 15, time.Minute)

从令牌提取声明

func (c *protectedController) GetUserInfo() (model.Response, error) {
	// 从上下文访问 JWT 声明
	jwtValue := c.Ctx.Values().Get("jwt")
	if jwtValue == nil {
		response := new(model.BaseResponse)
		response.SetCode(401)
		response.SetMessage("未授权")
		return response, nil
	}

	token := jwtValue.(*jwt.MapClaims)
	username := (*token)["username"].(string)
	role := (*token)["role"].(string)

	response := new(model.BaseResponse)
	response.SetData(map[string]interface{}{
		"username": username,
		"role":     role,
	})
	return response, nil
}

基于角色的访问控制

func (c *adminController) Before() {
	c.Middleware.Serve(c.Ctx)

	// 检查管理员角色
	jwtValue := c.Ctx.Values().Get("jwt")
	if jwtValue != nil {
		token := jwtValue.(*jwt.MapClaims)
		role := (*token)["role"].(string)
		if role != "admin" {
			c.Ctx.StatusCode(403)
			c.Ctx.JSON(map[string]string{
				"error": "禁止访问:需要管理员权限",
			})
			c.Ctx.StopExecution()
		}
	}
}

发送认证请求

在 Authorization 头中包含 JWT 令牌:

# 登录获取令牌
TOKEN=$(curl -s -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"password"}' | jq -r '.data.token')

# 发送认证请求
curl http://localhost:8080/api/profile \
  -H "Authorization: Bearer $TOKEN"

配置参考

属性 描述 默认值
jwt.private_key_path RSA 私钥路径 -
jwt.public_key_path RSA 公钥路径 -

下一步