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

go-zero(六) JWT鉴权

go-zero JWT鉴权

还记得我们之前登录功能,返回的信息是token吗? 这个token其实就是JSON Web Token简称JWT,它是一种开放标准(RFC 7519),用于在网络应用环境间安全地传递声明信息。

它是一种基于 JSON 的令牌,由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。签名是将头部和载荷组合后使用密钥生成的哈希值,用于验证令牌的真实性。

一、配置Auth

Auth是 jwt 密钥,过期等信息配置的 golang 结构体名称, 所以我们要在user-api.yaml中配置Auth

Auth:AccessSecret: "sss12345678" # AccessSecret的值要是8位以上的字符串AccessExpire: 10000 #AccessExpire是过期时间,单位是秒

注意:Auth的名字要和jwt: Auth 这个传入的一样。

接在我们在config.go文件里面定义用来解析配置文件的结构体

type Config struct {rest.RestConfMysqlDb struct {DbSource string `json:"dbSource"`}//增加Auth 结构体Auth struct {   AccessSecret stringAccessExpire int64}
}

到这边我们初步配置就完成了

二、生成JWT

为了简化操作,我们直接在loginlogic.go 代码中添加生成token的方法:

/*
secretKey string: 用于加密 JWT 的密钥,通常是一个足够复杂的随机字符串,确保 JWT 不易被伪造。
iat int64: 表示 JWT 的签发时间(Issued At),为一个时间戳,通常是当前时间的 UNIX 时间戳(以秒为单位),可以通过 time.Now().Unix() 获得。
seconds int64: 表示 JWT 的有效时长,单位为秒。这是通过 iat 参数计算出 JWT 的过期时间(exp)。
username string: 自定义有效载荷,通常是是使用用户唯一性的数据作为载体,这里我们为了方便演示使用了username。
*/func getJwtToken(secretKey string, iat, seconds int64,username string) (string, error) {// 创建一个 MapClaims 类型的声明claims := make(jwt.MapClaims)// 计算过期时间claims["exp"] = iat + seconds  // 设置 JWT 的过期时间(exp),通常需要一个 UNIX 时间戳claims["iat"] = iat            // 设置签发时间(iat)claims["username"] = username    // 自定义的负载(payload),可以设置为任何信息,例如用户名、用户ID等// 创建新的 JWTtoken := jwt.New(jwt.SigningMethodHS256) // 使用 HMAC SHA256 签名方法创建新的 JWT// 将声明分配给 JWTtoken.Claims = claims// 使用 secretKey 签名JWT,并返回生成的字符串和错误(如果有)return token.SignedString([]byte(secretKey))
}

JWT有三种加密方式分别是 :SigningMethodHS256SigningMethodHS384SigningMethodHS512 ,这里我们用的是hs256

然后修改Login中的代码,添加token生成功能。

go-zero中jwt的传递是在HTTP请求添加名为Authorization的header,形式如下 Authorization: Bearer <token> ,注意 Bearer <token> 之间有空格

func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {// todo: add your logic here and delete this line//因为我们目前还没涉及到jwt鉴权,所以先把token当面message使用userModel := l.svcCtx.UserModeluser, _ := userModel.FindOneByUsername(l.ctx, req.Username)//从配置文件中获取secret 、secret secret := l.svcCtx.Config.Auth.AccessSecretexpire := l.svcCtx.Config.Auth.AccessExpire//生成jwt tokentoken, err := getJwtToken(secret, time.Now().Unix(), expire, req.Username)if err != nil {return nil, errors.New(4, "token生成失败")}//查询username判断是否有数据if user != nil {//如果有数据,密码是否和数据库匹配if req.Password == user.Password {return &types.LoginResponse{Token:"Bearer "+ token, //开头添加Bearer }, nil} else {return nil, errors.New(5, "密码错误")}} else {return nil, errors.New(6, "用户未注册")}
}

注意:从后面我们响应信息默认使用 xhttp.JsonBaseResponseCtx() 即zeromicro的x库

运行项目,测试结果如下:
在这里插入图片描述

JWT验证go-zero自动帮我们做了,所以我们不需要单独的去写一个验证方法。

三、使用jwt

注册和登录的功能已经实现,现在我们来实现用户的查询和更新功能,这两个功能在正常的业务逻辑中都是需要通过登录实现的,也就是说需要使用jwt来验证和传递信息。

现在我们来编辑新的api文件,并演示如何启用jwt, 在api中我们通过可选参数【jwt:Auth 】来控制是否启用 JWT。具体实现如下:

syntax = "v1"/*
省略注册和登录
*///因为我们想要通过jwt来传递数据,所以我们不需求请求信息
type (GetInfoResponse {Username string `json:"username" `Password string `json:"password" `}
)type (//更新数据我们就简单修改下密码UpdataRequest {Password string `json:"password" `}UpdataResponse {Message string `json:"message"`}
)@server (group:  userprefix: /v1jwt:    Auth //开启jwt
)
service user-api {@handler GetInfoHandler// 不需要请求信息post /getinfo returns (GetInfoResponse)@handler UpdataHandlerpost /updata (UpdataRequest) returns (UpdataResponse)
}

接下来我们使用goctl生成新的代码

goctl api go --api user.api --dir ./

我们来看下go-zero是怎么帮我们实现jwt认证的,我们先看下routes.go 文件。

func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {/*省略注册和登录的代码*/server.AddRoutes([]rest.Route{{Method:  http.MethodPost,Path:    "/getinfo",Handler: getinfo.GetInfoHandler(serverCtx),},},//开启jwtrest.WithJwt(serverCtx.Config.Auth.AccessSecret),rest.WithPrefix("/v1"),)server.AddRoutes([]rest.Route{{Method:  http.MethodPost,Path:    "/updata",Handler: updata.UpdataHandler(serverCtx),},},//开启jwtrest.WithJwt(serverCtx.Config.Auth.AccessSecret),rest.WithPrefix("/v1"),)
}

goctl 帮我在/getinfo/updata两个路由使用rest.WithJwt开启了jwt验证。

四、获取载体信息

1. 实现信息查询

我们首先去实现查询和更新这两个业务逻辑,到logic目录下打开getinfologic.go 修改代码如下:

func (l *GetInfoLogic) GetInfo() (resp *types.GetInfoResponse, err error) {// todo: add your logic here and delete this line//使用l.ctx.Value() 来获取jwt的内容username := l.ctx.Value("username").(string)usermodel := l.svcCtx.UserModeluser, err := usermodel.FindOneByUsername(l.ctx, username)if err != nil && err != model.ErrNotFound {return nil, errors.New(0, "数据库连接失败")}if user == nil {return nil, errors.New(11, "查询失败,没有该用户信息")}return &types.GetInfoResponse{Username: user.Username,Password: user.Password,}, nilreturn
}

在go-zero中使用l.ctx.Value()来获取数据, 这里我们使用的username,其实就是我们获取token的时候传入的自定义payload, 总而言之就是你传什么字段就获取什么字段。因为Value()any类型,所以需要对它进行断言。

我们先不传递Authorization的值,运行项目测试一下,可以发现直接报错了
在这里插入图片描述

我们可以看下运行日志,提示我们认证失败,没有token

在这里插入图片描述

现在添加Authorization的值再测试下,可以看到成功执行:
在这里插入图片描述

2. 实现信息修改

func (l *UpdataLogic) Updata(req *types.UpdataRequest) (resp *types.UpdataResponse, err error) {// todo: add your logic here and delete this lineusername := l.ctx.Value("username").(string)usermodel := l.svcCtx.UserModeluser, err := usermodel.FindOneByUsername(l.ctx, username)if err != nil && err != model.ErrNotFound {return nil, errors.New(0, "数据库连接失败")}if user == nil {return nil, errors.New(11, "查询失败,没有该用户信息")}newUser := model.Users{Id:        user.Id,Username:  user.Username,Password:  req.Password, //使用请求响应的密码CreatedAt: user.CreatedAt,}err = usermodel.Update(l.ctx, &newUser)if err != nil {return nil, errors.New(12, "数据更新失败")}return &types.UpdataResponse{Message: "数据更新成功",}, nil}

运行项目测试:

在这里插入图片描述

五、JWT 认证失败自定义处理返回

刚刚我们测试了一下JWT认证失败的情况,它直接给我们返回来401代码,这显然不是我们想要的,现在我们来看下JWT 认证失败自定义处理返回。

我们需要在main中定义一个callback,并注册到服务中 , rest.WithUnauthorizedCallback 会全局捕捉jwt认证失败的请求

    server := rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(func(w http.ResponseWriter, r *http.Request, err error) {

接下来我们来具体实现它:

//定义一个返回信息
func unauthorizedHandler(w http.ResponseWriter, r *http.Request, err error) {xhttp.JsonBaseResponse(w, "认证失败:"+err.Error())
}func main() {/*....*/
// 在main函数中添加一个callbackserver := rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(unauthorizedHandler))// 自定义处理返回}))/*....*/
}

运行项目,再次测试认证失败的情况:
在这里插入图片描述

http://www.lryc.cn/news/489962.html

相关文章:

  • 做一个FabricJS.cc的中文文档网站——面向markdown编程
  • 开发 + 安全:网络安全的协作方法
  • Next.js- App Router 概览
  • python oa服务器巡检报告脚本的重构和修改(适应数盾OTP)有空再去改
  • 【工控】线扫相机小结 第四篇
  • 亲测解决Unpack operator in subscript requires Python 3.11 or newer
  • 数据结构 ——— 堆排序算法的实现
  • On-Chip-Network之Topology
  • 2024年11月21日Github流行趋势
  • 第三十八章 IOT 通信协议MQTT协议实现的中间件EMQXDocker安装与验证指南
  • Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复
  • CodiMD导出pdf失败或无中文
  • 数字图像处理(2):Verilog基础语法
  • Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化
  • 爬虫重定向问题解决
  • Java技术复习提升 10异常
  • 真题-桂城2022年五年级
  • android 使用MediaPlayer实现音乐播放--权限请求
  • Web开发:ORM框架之使用Freesql的DbFrist封装常见功能
  • 【多线程-第一天-多线程的执行原理-多线程的优缺点-主线程 Objective-C语言】
  • SQL基础语法介绍-基于MySQL
  • 一分钟学习数据安全——数据安全风险的系统化应对思路
  • 端口port常识
  • 【Oracle实战】文章导读
  • “人工智能+高职”:VR虚拟仿真实训室的发展前景
  • c语言学习27宏定义条件编译
  • 理论结合实践:用Umami构建网站分析系统
  • Unreal从入门到精通之如何绘制用于VR的3DUI交互的手柄射线
  • 移动充储机器人“小奥”的多场景应用(上)
  • 已解决wordpress提示正在执行例行维护,请一分钟后回来