Gin实战4:手把手打造安全登录接口,JWT身份验证


🎯 导语

登录接口是系统安全的门户,JWT则是现代微服务架构的通行证。本文带你从0到1实现Gin框架下的安全认证体系,掌握JWT的核心玩法与实践!

一、前置知识准备

二、登录接口实战开发

STEP 1:Server层核心代码

  • 接收请求参数并校验,请求biz 逻辑层进行登录验证与jwt令牌生成
    var req LoginRequest
	err := ctx.ShouldBind(&req)
	if err != nil {
		ctx.JSON(400, gin.H{
			"msg": err.Error(),
		})
		return
	}
	if req.Username == "" || req.Password == "" {
		ctx.JSON(400, gin.H{
			"msg": "用户名或密码不能为空",
		})
		return
	}
	token, err := a.account.Login(ctx, &biz.Account{
		Username: req.Username,
		Password: req.Password,
	})
	if err != nil {
		ctx.JSON(500, gin.H{
			"msg": err.Error,
		})
		return
	}
	ctx.JSON(200, gin.H{
		"token": token,
	})

STEP 2:Biz 层核心代码 1、登录逻辑处理;查询用户并验证密码(密码通常不需要明文存储,而是通过哈希算法加密存储)

func (a *AccountUseCase) Login(ctx context.Context, account *Account) (string, error) {
	acc, err := a.repo.GetByUsername(ctx, account.Username)
	if err != nil {
		return "", errors.New("用户名或密码错误")
	}
	if acc.Password != account.Password {
		return "", errors.New("用户名或密码错误")
	}
	return a.GenerateJWT(a.jwt.Secret, acc.UserID, acc.Username)
}

STEP 3:生成JWT 令牌

  1. 定义 CustomClaims 结构体,用于存储用户信息,并继承jwt.RegisteredClaims结构体,实现JWT令牌的基本信息。
type CustomClaims struct {
	UserID   int    `json:"user_id"`
	Username string `json:"username"`
	jwt.RegisteredClaims
}

2、GenerateJWT 方法 生成JWT令牌

// 设置签名算法(这里用HS256示例)
	signingMethod := jwt.SigningMethodHS256

	// 创建Claims
	claims := CustomClaims{
		UserID:   userID,
		Username: username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(a.jwt.Expire)), // 过期时间
			IssuedAt:  jwt.NewNumericDate(time.Now()),                   // 签发时间
			Issuer:    "study_gin",                                      // 签发者
		},
	}
	// 创建Token对象
	token := jwt.NewWithClaims(signingMethod, claims)

	// 生成签名字符串
	tokenString, err := token.SignedString([]byte(secret))
	if err != nil {
		return "", err
	}

	return tokenString, nil

STEP 4:数据层模拟

  • 模拟返回固定数据
func (a *AccountRepo) GetByUsername(ctx context.Context, username string) (*biz.Account, error) {
	return &biz.Account{
		Username: "admin",
		Password: "123@456",
		UserID:   1,
	}, nil
}

STEP 5:routers 层注册


r.POST("/v1/login", account.Login)

四、重新编译依赖

  • wireApp 函数
func wireApp(server *conf.Server, logger logger.Logger) (*http.Server, error) {
	panic(wire.Build(
		service.ProviderSet,
		biz.ProviderSet,
		data.ProviderSet,
		routers.InitRouters,
		newHttpServer))
}
  • 执行 make generate
  • 执行 make run

五、接口测试

1、使用Apifox Apifox

2、使用curl

curl --location --request POST 'http://localhost:8080/v1/login?username=admin&password=123@456' \
--header 'Accept: */*' \
--header 'Host: localhost:8080' \
--header 'Connection: keep-alive'

📢 读者互动

“你在实现JWT时踩过哪些坑?欢迎留言分享!公众号后台回复「Gin实战」获取”

wx

关注公众号

©2017-2023 鲁ICP备17023316号-1 Powered by Hugo