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

🎯 导语
登录接口是系统安全的门户,JWT则是现代微服务架构的通行证。本文带你从0到1实现Gin框架下的安全认证体系,掌握JWT的核心玩法与实践!
一、前置知识准备
- JWT结构解析(Header/Payload/Signature)
- Gin实战1:环境搭建与项目初始化
- Gin实战2:项目配置管理
- Gin实战3:系统日志管理
二、登录接口实战开发
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 令牌
- 定义 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
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实战」获取”
