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.mzph.cn/web/60148.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ZYNQ程序固化——ZYNQ学习笔记7

一、ZYNQ启动过程 二、 SD卡启动实操 1、对ZYNQ进行配置添加Flash 2、添加SD卡 3、重新生成硬件信息 4、创建vitis工程文件 5、勾选板级支持包 6、对系统工程进行整体编译&#xff0c;生成两个Debug文件&#xff0c;如图所示。 7、插入SD卡&#xff0c;格式化为 8、考入BOOT.…

进程其他知识点

/* #include <stdlib.h> void exit(int status); #include <unistd.h> void _exit(int status); status 参数&#xff1a;是进程退出时的一个状态信息。父进程回收子进程资源的时候可以获取到。 */ #include <stdio.h> #include <stdlib.h> #include &…

Android ART知多少?

Android 虚拟机 ART&#xff08;Android Runtime&#xff09;是 Android 平台上的应用程序运行时环境&#xff0c;用于执行应用程序的字节码。ART 自 Android 5.0&#xff08;Lollipop&#xff09;开始取代了 Dalvik&#xff0c;成为 Android 的默认运行时环境。本文将从以下几…

C++ —— 剑斩旧我 破茧成蝶—C++11

江河入海&#xff0c;知识涌动&#xff0c;这是我参与江海计划的第2篇。 目录 1. C11的发展历史 2. 列表初始化 2.1 C98传统的{} 2.2 C11中的{} 2.3 C11中的std::initializer_list 3. 右值引用和移动语义 3.1 左值和右值 3.2 左值引用和右值引用 3.3 引用延长生命周期…

推荐15个2024最新精选wordpress模板

以下是推荐的15个2024年最新精选WordPress模板&#xff0c;轻量级且SEO优化良好&#xff0c;适合需要高性能网站的用户。中文wordpress模板适合搭建企业官网使用。英文wordpress模板&#xff0c;适合B2C网站搭建&#xff0c;功能强大且兼容性好&#xff0c;是许多专业外贸网站的…

(计算机毕设)基于SpringBoot+Vue的房屋租赁系统的设计与实现

博主可接毕设设计&#xff01;&#xff01;&#xff01; 各种毕业设计源码只要是你有的题目我这里都有源码 摘 要 社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。互…

python蓝桥杯刷题2

1.最短路 题解&#xff1a;这个采用暴力枚举&#xff0c;自己数一下就好了 2.门牌制作 题解&#xff1a;门牌号从1到2020&#xff0c;使用for循环遍历一遍&#xff0c;因为range函数无法调用最后一个数字&#xff0c;所以设置成1到2021即可&#xff0c;然后每一次for循环&…

深度学习中的Pixel Shuffle和Pixel Unshuffle:图像超分辨率的秘密武器

在深度学习的计算机视觉任务中&#xff0c;提升图像分辨率和压缩特征图是重要需求。Pixel Shuffle和Pixel Unshuffle是在超分辨率、图像生成等任务中常用的操作&#xff0c;能够通过转换空间维度和通道维度来优化图像特征表示。本篇文章将深入介绍这两种操作的原理&#xff0c;…

Unity类银河战士恶魔城学习总结(P132 Merge skill tree with skill Manager 把技能树和冲刺技能相组合)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了解锁技能后才可以使用技能&#xff0c;先完成了冲刺技能的锁定解锁 Dash_Skill.cs using System.Collections; using System…

正则表达式完全指南,总结全面通俗易懂

目录 元字符 连接符 限定符 定位符 修饰符&#xff08;标记&#xff09; 运算符优先级 普通字符集及其替换 零宽断言 正向先行断言 负向先行断言 正向后发断言 负向后发断言 捕获组 普通捕获组 命名捕获组 PS:非捕获组 正则表达式在线测试: 正则在线测试工具 …

qt之QFTP对文件夹(含嵌套文件夹和文件)、文件删除下载功能

一、前言 主要功能如下&#xff1a; 1.实现文件夹的下载和删除&#xff0c;网上很多资料都是单独对某个路径的文件操作的&#xff0c;并不能对文件夹操作 2.实现目标机中含中文名称自动转码&#xff0c;有些系统编码方式不同&#xff0c;下载出来的文件会乱码 3.实现ftp功能…

HCIP --OSI七层参考模型回顾、TCP/UDP协议复习

目录 一、OSI 二、重要的三个协议报头格式 名词注解 MTU 封装 解封装 PDU ARP DNS TCP/IP与OSI的区别 三、数据包转发过程 四、获取目标ip地址方式 五、获取目标mac地址方式 六、交换机的工作原理 七、TCP/UDP TCP&#xff08;Transmission Control Protocol&a…

物联网智能技术的深入探讨与案例分析

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Keil基于ARM Compiler 5的工程迁移为ARM Compiler 6的工程

环境&#xff1a; keil版本为5.38&#xff0c;版本务必高于5.30 STM32F4的pack包版本要高于2.9 软件包下载地址&#xff1a;https://zhuanlan.zhihu.com/p/262507061 一、更改Keil中编译器 更改后编译&#xff0c;会报很多错&#xff0c;先不管。 二、更改头文件依赖 观察…

9.《滑动窗口篇》---①长度最小的子数组(中等)

滑动窗口推导过程 我们不能说一上来就知道这个题目用滑动窗口&#xff0c;然后就使用滑动窗口的方法来做这个题目。 首先我们想到的应该是暴力解法。 接着再优化为滑动窗口 由于数字都是 ≥ 0 的数。因此累加的数越多。和越大。 因此right往后遍历的时候。当发现sum > targe…

Marin说PCB之电源完整性之电源网络的PDN仿真CST---04

小编我最近都要忙疯了&#xff0c;好不容易去韩国出个差&#xff0c;打算不忙的时候去首尔看看韩国的美女们&#xff0c;说错了&#xff0c;是看美景啊。谁料想韩国分公司的SI同事的李相赫同志由于结婚请假了一个多月啊&#xff0c;他倒是挺爽啊&#xff0c;和老婆去度蜜月了&a…

视频融合×室内定位×数字孪生

随着物联网技术的迅猛发展&#xff0c;室内定位与视频融合技术在各行各业中得到了广泛应用。不仅能够提供精确的位置信息&#xff0c;还能通过实时视频监控实现全方位数据的可视化。 与此同时&#xff0c;数字孪生等技术的兴起为智慧城市、智慧工厂等应用提供了强大支持&#…

git push时报错! [rejected] master -> master (fetch first)error: ...

错误描述&#xff1a;在我向远程仓库push代码时&#xff0c;即执行 git push origin master命令时发生的错误。直接上错误截图。 错误截图 错误原因&#xff1a; 在网上查了许多资料&#xff0c;是因为Git仓库中已经有一部分代码&#xff0c;它不允许你直接把你的代码覆盖上去…

概念解读|K8s/容器云/裸金属/云原生...这些都有什么区别?

随着容器技术的日渐成熟&#xff0c;不少企业用户都对应用系统开展了容器化改造。而在容器基础架构层面&#xff0c;很多运维人员都更熟悉虚拟化环境&#xff0c;对“容器圈”的各种概念容易混淆&#xff1a;容器就是 Kubernetes 吗&#xff1f;容器云又是什么&#xff1f;容器…

【Android】线程池的解析

引言 在Android当中根据用途分为主线程与子线程&#xff0c;主线程当中主要处理与界面相关的操作&#xff0c;子线程主要进行耗时操作。除了Thread本身以外&#xff0c;在Android当中还有很多扮演者线程的角色&#xff0c;比如AsyncTask&#xff08; 底层为线程池&#xff0c;…