goframe开发一个企业网站 TOKEN 的使用11
TOKEN的使用
在config.yame加入一个
jwt:
secret-key: "%G!NX4jUeg9vHs#ltaaq8Kj8v#9P%7IB"```
为什么需要密钥?
- 防止篡改
如果有人修改了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()
}
代码还要再写,现在只是最初版本。