当前位置: 首页 > article >正文

goframe开发一个企业网站 TOKEN 的使用11

TOKEN的使用

在config.yame加入一个


jwt:
  secret-key: "%G!NX4jUeg9vHs#ltaaq8Kj8v#9P%7IB"```

为什么需要密钥?

  1. 防止篡改
    如果有人修改了JWT的内容
    由于没有正确的密钥,无法生成匹配的签名
    服务器可以检测到篡改
    确保来源可信
    只有持有密钥的服务器才能生成有效的JWT
    可以确保令牌确实来自可信的源
    密钥泄露的风险:
    如果密钥泄露,攻击者可能:
    伪造新的JWT
    修改现有JWT
    冒充合法用户
    获取未经授权的访问权限
    这就是为什么要:
    使用强随机密钥
    定期更换密钥
    安全存储密钥
    不同环境使用不同密钥

Toke的生成及解析

这里因为logic 的usr.go发生改变。我们重新改写:

type CommonReq struct {
	Authorization string `p:"Authorization" in:"header" dc:"Bearer {{token}}"`
}






package user

import (
	"context"
	"gf_new_web/api/common"
	"gf_new_web/internal/dao"
	"gf_new_web/internal/service"
	"strings"
	"time"

	"github.com/gogf/gf/v2/crypto/gmd5"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/golang-jwt/jwt/v5"
)

type sUser struct{}

func init() {
	service.RegisterUser(New())
}

func New() *sUser {
	return &sUser{}
}

// Login 用户登录
func (s *sUser) Login(ctx context.Context, req *common.UserLoginReq) (*common.ReturnJsRes, error) {
	// TODO: 实现登录逻辑和生成token
	// 获取用户记录
	record, err := dao.User.Ctx(ctx).Where("username", req.Username).One()
	if err != nil {
		return nil, err
	}
	// 对输入密码进行MD5加密
	inputPassword := gmd5.MustEncrypt(req.Password)
	// 比较密码是否匹配
	if record["password"].String() != inputPassword {
		return nil, gerror.New("用户名或密码错误")
	}
	// 生成token
	claims := jwt.MapClaims{
		"id":       record["id"].Int(),
		"username": record["username"].String(),
		"email":    record["email"].String(),
		"exp":      time.Now().Add(time.Hour * 24).Unix(), // 24小时过期
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString([]byte(g.Cfg().MustGet(ctx, "jwt.secret-key").String()))
	if err != nil {
		return nil, err
	}
	return &common.ReturnJsRes{
		Data: tokenString,
	}, nil

}

// Register 用户注册
func (s *sUser) Register(ctx context.Context, req *common.UserRegisterReq) (*common.ResultRes[int64], error) {

	req.Username = strings.TrimSpace(req.Username)
	req.Password = strings.TrimSpace(req.Password)
	// 检查用户名是否已存在
	count, err := dao.User.Ctx(ctx).Where("username", req.Username).Count()
	if err != nil {
		return nil, err
	}
	if count > 0 {
		return nil, gerror.New("该手机号已被注册")
	}

	result, err := dao.User.Ctx(ctx).Data(g.Map{
		"username": req.Username,
		"password": gmd5.MustEncrypt(req.Password),
	}).Insert()
	if err != nil {
		return nil, err
	}
	id, err := result.LastInsertId()
	if err != nil {
		return nil, err
	}
	return &common.ResultRes[int64]{
		Data: id,
	}, nil
}

// GetInfo 获取用户信息
func (s *sUser) GetInfo(ctx context.Context, req *common.UserInfoReq) (*common.ReturnJsRes, error) {
	record, err := dao.User.Ctx(ctx).Where("id", req.Id).One()
	if err != nil {
		return nil, err
	}
	return &common.ReturnJsRes{
		Data: record,
	}, nil
}

// Update 更新用户信息
func (s *sUser) Update(ctx context.Context, req *common.UserUpdateReq) (*common.ReturnJsRes, error) {
	data := g.Map{}
	if req.Username != "" {
		data["username"] = req.Username
	}
	if req.Password != "" {
		data["password"] = req.Password // TODO: 需要加密处理
	}
	// 修复:声明 err 变量
	_, err := dao.User.Ctx(ctx).Data(data).Where("id", req.Id).Update()
	return &common.ReturnJsRes{
		Msg:  "更新成功",
		Data: req,
	}, err
}

// Delete 删除用户
func (s *sUser) Delete(ctx context.Context, req *common.UserDeleteReq) (*common.ReturnJsRes, error) {
	row, err := dao.User.Ctx(ctx).Where("id", req.Id).Delete()
	return &common.ReturnJsRes{
		Msg:  "删除成功",
		Data: row,
	}, err
}

// GetList 获取用户列表
func (s *sUser) GetList(ctx context.Context, req *common.UserListReq) (*common.ReturnJsRes, error) {
	m := dao.User.Ctx(ctx)

	// 获取总数
	total, err := m.Count()
	if err != nil {
		return nil, err
	}

	// 分页查询
	records, err := m.Page(req.Page, req.Size).Order("id DESC").All()
	if err != nil {
		return nil, err
	}

	// 构造返回数据
	var list []common.UserInfoRes
	for _, record := range records {
		list = append(list, common.UserInfoRes{
			Id:       record["id"].Int(),
			Username: record["username"].String(),
		})
	}

	return &common.ReturnJsRes{
		Data: g.Map{
			"page":  req.Page,
			"size":  req.Size,
			"list":  list,
			"total": total,
		},
	}, nil
}

// GetTokenInfo 从token中解析用户信息
func (s *sUser) GetToken(ctx context.Context, res *common.CommonReq) (map[string]interface{}, error) {

	// 去掉Bearer前缀和空格
	token := res.Authorization
	token = strings.TrimPrefix(token, "Bearer ")
	token = strings.TrimSpace(token)

	// 解析JWT token
	parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
		return []byte(g.Cfg().MustGet(ctx, "jwt.secret-key").String()), nil
	})

	if err != nil || !parsedToken.Valid {
		return nil, gerror.New("无效的token")
	}

	claims := parsedToken.Claims.(jwt.MapClaims)

	// 直接返回map
	return g.Map{
		"id":       gconv.Int(claims["id"]),
		"username": gconv.String(claims["username"]),
		"email":    gconv.String(claims["email"]),
		"exp":      gconv.Int(claims["exp"]),
	}, nil
}

登陆后则生成token 有为了限制登陆,我们可以将其写,这里一切从简,不进行判断。
生成serivce: gf gen service .

在路由进行token判断

修改路由加入中间件进行token的判断:

package router

import (
	"gf_new_web/api/common"
	"gf_new_web/internal/controller/admin"
	"gf_new_web/internal/service"

	"github.com/gogf/gf/v2/net/ghttp"
)

func InitAdminRouter(s *ghttp.Server) {
	s.Group("/admin", func(group *ghttp.RouterGroup) {
		group.Middleware(ghttp.MiddlewareHandlerResponse)
		group.POST("/reg", admin.Index.Reg)
		group.POST("/login", admin.Index.Login)
		group.POST("/get-token-info", admin.Base.GetTokenInfo)
		group.Middleware(authMiddleware)
		group.GET("/user", admin.Index.GetInfo)

	})
}

// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6IiIsImV4cCI6MTczMTE2MzQ4NSwiaWQiOjEsInVzZXJuYW1lIjoiMTc3MDU4MTI1MDAifQ.AjRhovvDw7LhNkB-SWn98JJar0cXS-oF7NVr3wBHY2M

// ... existing code ...
func authMiddleware(r *ghttp.Request) {
	// 获取token
	token := r.Header.Get("Authorization")
	if token == "" {
		r.Response.WriteJson(common.ReturnJsRes{
			Code: 1,
			Msg:  "Authorization token不能为空",
		})
		r.Exit()
		return
	}

	// 验证token
	tokenInfo, err := service.User().GetToken(r.Context(), &common.CommonReq{
		Authorization: token,
	})
	if err != nil {
		r.Response.WriteJson(common.ReturnJsRes{
			Code: 1,
			Msg:  err.Error(),
		})
		r.Exit()
		return
	}

	if code, ok := tokenInfo["code"].(int); ok && code != 0 {
		r.Response.WriteJson(tokenInfo)
		r.Exit()
		return
	}

	r.Middleware.Next()
}

代码还要再写,现在只是最初版本。


http://www.kler.cn/a/385438.html

相关文章:

  • Java | Leetcode Java题解之第552题学生出勤记录II
  • element plus中修改el-table的样式
  • docker-compose.yml 文件来配置 Redis
  • 基于IM场景下的Wasm初探:提升Web应用性能|得物技术
  • mysql删除语句:@Update(“TRUNCATE TABLE employee“)讲解
  • 内置函数【MySQL】
  • 从0开始学习机器学习--Day15--梯度检验以及随机初始化
  • 【手势识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+机器学习+Django网页界面+算法模型
  • uniapp 整合 OpenLayers - 使用modify修改要素
  • Java教学新动力:SpringBoot辅助平台
  • DAY22|回溯算法Part01|LeetCode: 77. 组合、216.组合总和III 、17.电话号码的字母组合
  • 2024年入职_转行网络安全,该如何规划?
  • OJ06:206.反转链表
  • 保障性住房管理:SpringBoot技术优势分析
  • 化工厂主要涉及的自动化备件有哪些?
  • go语言中package详解
  • MySQL 8.0中的‘caching_sha2_password‘身份验证插件问题
  • Gemini 对话机器人加入开源盲水印技术来检测 AI 生成的内容
  • Scala包对象。
  • java导出word文件(手绘)
  • GCC和clang的爱恨情仇
  • 【Linux系统】—— 基本指令(二)
  • SpringBoot实现的企业资产管理系统
  • 纯C++信号槽使用Demo (sigslot 库使用)
  • PyQt入门指南四十六 性能优化策略
  • AntPathMatcher 技术文档