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有三种加密方式分别是 :SigningMethodHS256
、SigningMethodHS384
、SigningMethodHS512
,这里我们用的是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))// 自定义处理返回}))/*....*/
}
运行项目,再次测试认证失败的情况:
相关文章:

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

做一个FabricJS.cc的中文文档网站——面向markdown编程
📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,用爱发电&#…...

开发 + 安全:网络安全的协作方法
开发团队和安全团队之间由来已久的紧张关系一直是组织内部摩擦的根源。开发人员优先考虑速度和效率,旨在通过快节奏、迭代的开发周期快速交付功能和产品并高效前进。另一方面,安全团队努力平衡风险和创新,但必须专注于使用护栏保护敏感数据和…...

Next.js- App Router 概览
#题引:我认为跟着官方文档学习不会走歪路 一:App Router与Page Router 在 v13 版本中,Next.js 引入了一个基于 React 服务器组件 构建的新的 App Router,而在这之前,Next.js 使用的是Page Router。 目录结构 pages …...

python oa服务器巡检报告脚本的重构和修改(适应数盾OTP)有空再去改
Two-Step Vertification required: Please enter the mobile app OTPverification code: 01.因为巡检的服务器要双因子认证登录,也就是登录堡垒机时还要输入验证码。这对我的巡检查服务器的工作带来了不便。它的机制是每一次登录,算一次会话…...

【工控】线扫相机小结 第四篇
背景 这一片主要是对第三篇继续补充。话说上一篇讲到了两种模式的切换,上一篇还遗留了一个Bug,在这一篇里进行订正! 代码回顾 /// <summary>/// 其实就是打开触发/// </summary>void SetLineSacanWorkMode(){-----首先设置为帧…...
亲测解决Unpack operator in subscript requires Python 3.11 or newer
这个问题是在小虎想提前定义一个list,然后作为index list来调用另一个list里面的变量出现的问题。 环境 Ubuntu 22.04 + python 3.10 故障代码示例 NoneList = [None] * opt.spatial_dims TargetMask = Target[i] == torch.arange(1...

数据结构 ——— 堆排序算法的实现
目录 前言 向下调整算法(默认建大堆) 堆排序算法的实现(默认升序) 前言 在之前几章学习了如何用向上调整算法和向下调整算法对数组进行建大/小堆数据结构 ——— 向上/向下调整算法将数组调整为升/降序_对数组进行降序排序代码…...

On-Chip-Network之Topology
片上网络拓扑决定了网络中节点和通道之间的物理布局和连接。拓扑对整体网络性价比的影响是巨大的。拓扑决定了消息 必须经过的跳数(或路由器)以及跳数之间的互连长度,从而显著影响网络延迟。由于经过路由器和链路会产生功耗,因此 …...

2024年11月21日Github流行趋势
项目名称:twenty 项目维护者:charlesBochet, lucasbordeau, Weiko, FelixMalfait, bosiraphael项目介绍:正在构建一个由社区支持的现代化Salesforce替代品。项目star数:21,798项目fork数:2,347 项目名称:p…...
第三十八章 IOT 通信协议MQTT协议实现的中间件EMQXDocker安装与验证指南
EMQX概述以及Docker安装与验证指南 一、EMQX概述 EMQX(原名EMQ X),是一款完全开源、高度可伸缩、高可用的分布式MQTT消息服务器。它不仅支持MQTT协议,还兼容CoAP/LwM2M等多种物联网协议,是5G时代万物互联的重要消息引擎。这款软件由杭州映云科技有限公司开发,基于Erlan…...

Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复
目录 安装包 flume的部署 负载均衡测试 故障恢复 安装包 在这里给大家准备好了flume的安装包 通过网盘分享的文件:apache-flume-1.9.0-bin.tar.gz 链接: https://pan.baidu.com/s/1DXMA4PxdDtUQeMB4J62xoQ 提取码: euz7 --来自百度网盘超级会员v4的分享 ----…...
CodiMD导出pdf失败或无中文
CodiMD导出pdf失败,弹出文件保存窗口,有个pdf文件能下载,但是保存的时候提示“网站出问题了”,实际到服务器上看会发现docker崩溃了。 解决办法: 使用最新的CodiMD镜像,如nabo.codimd.dev/hackmdio/hackmd:…...

数字图像处理(2):Verilog基础语法
(1)Verilog常见数据类型: reg型、wire型、integer型、parameter型 (2)Verilog 常见进制:二进制(b或B)、十进制(d或D)、八进制(o或O)、…...

Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化
Kafka:分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析:从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析:…...

爬虫重定向问题解决
一,问题 做爬虫时会遇到强制重定向的链接,此时可以手动获取重定向后的链接 如下图情况 第二个链接是目标要抓取的,但它是第一个链接重定向过去的,第一个链接接口状态也是302 二,解决方法 请求第一个链接࿰…...

Java技术复习提升 10异常
10 异常 10.1异常介绍及分类 异常捕获 选中后alttabt->选中try-catch 异常就是程序执行中不正常的情况 注意语法和逻辑错误并不是异常 异常分类有两种 error和exception error是错误 虚拟机无法解决的严重问题 exception是其他因为编程错误或者外在因素导致的一般性的问…...

真题-桂城2022年五年级
目录 GC.2022.五年级.01.拍7 输入数据 1 输出数据 1 GC.2022.五年级.02.硬币 输入数据 1 输出数据 1 答案: GC.2022.五年级.03.次大公约数 输入数据 1 输出数据 1 GC.2022.五年级.04.显示器 输入数据 1 输出数据 1 GC.2022.五年级.05.数对 输入数据 1 输…...

android 使用MediaPlayer实现音乐播放--权限请求
在Android应用中,获取本地音乐文件的权限是实现音乐扫描功能的关键步骤之一。随着Android版本的不断更新,从Android 6.0(API级别23)开始,应用需要动态请求权限,而到了android 13以上需要的权限又做了进一步…...
Web开发:ORM框架之使用Freesql的DbFrist封装常见功能
一、调用 public class Program {static string connectionstring "连接字符串(数据库名)";static void Main(string[] args){//1.连接数据库var freesql new FreeSqlBuilder().UseConnectionString(DataType.SqlServer, connectionstring…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...