go-zerogo web集成JWT和cobra命令行工具实战

news2024/11/18 1:51:18

前言

上一篇:从零开始基于go-zero的go web项目实战-01项目初始化

从零开始基于go-zero搭建go web项目实战-02集成JWT和cobra命令行工具

源码仓库地址 源码 https://gitee.com/li_zheng/treasure-box

JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用间传递声明式信息。它是一种基于JSON的轻量级的身份验证和授权机制,用于在客户端和服务器之间安全地传输信息。《JSON Web Tokens》
go-zero中默认引入了golang-jwt依赖,以中间件的形式,可以给请求配置jwt校验,需要开发人员自己编写生成token的逻辑和接口,接下来从用户登录、验证、token生成、接口校验进行讲解。

实体声明

定义一个TokenClaims 结构体,承载token信息

type TokenClaims struct {
	UserId   int64
	UserName string
	UserType string
	Role     []string
	Token    string
	Ext      map[string]any
	jwt.RegisteredClaims
}

定义一个UserLoginReq 结构体,保存用户名和密码

type UserLoginReq struct {
	Username string `form:"username"`
	Password string `form:"password"`
}

编写生成token的工具方法

编写一个工具方法,用于通过密钥和时间生成token

package util

import (
	"github.com/golang-jwt/jwt/v4"
	"github.com/zachary/tbox/internal/types"
	"time"
)

// GetJwtToken
// @secretKey: JWT 加解密密钥
// @seconds: 过期时间,单位秒
// @token: 数据载体
func GetJwtToken(secretKey string, seconds int64, token *types.TokenClaims) (string, error) {
	token.RegisteredClaims = jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(seconds))),
		Issuer:    "zachary",
		IssuedAt:  jwt.NewNumericDate(time.Now()),
		NotBefore: jwt.NewNumericDate(time.Now()),
	}
	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, token)
	mySigningKey := []byte(secretKey)
	signedString, err := claims.SignedString(mySigningKey)
	return signedString, err
}

声明一个post的login接口进行登录校验

登录业务逻辑编写(临时登录后续进行优化)


// handler.go

package login

import (
	"github.com/zachary/tbox/internal/svc"
	"github.com/zachary/tbox/internal/types"
	"github.com/zachary/tbox/internal/util"
	"github.com/zachary/tbox/internal/web"
	"github.com/zeromicro/go-zero/rest/httpx"
	"net/http"
)

var tempUser = types.UserLoginReq{
	Username: "admin",
	Password: "admin",
}

func TempUpLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		var req types.UserLoginReq
		if err := httpx.Parse(r, &req); err != nil {
			httpx.ErrorCtx(r.Context(), w, err)
			return
		}
		if req.Username == tempUser.Username && req.Password == tempUser.Password {
			// 验证成功,生成测试数据
			token := types.TokenClaims{
				UserId:   1000,
				UserName: tempUser.Username,
				UserType: "WEB",
				Role:     []string{"admin"},
				Ext:      map[string]any{"ex1": "val1", "ext2": "val2"},
			}
			secret := svcCtx.Config.Auth.AccessSecret
			expire := svcCtx.Config.Auth.AccessExpire
			jwtToken, err := util.GetJwtToken(secret, expire, &token)
			if err != nil {
				httpx.ErrorCtx(r.Context(), w, err)
				return
			}
			token.Token = jwtToken
			httpx.OkJsonCtx(r.Context(), w, web.Success(token))
		} else {
			httpx.WriteJsonCtx(r.Context(), w, http.StatusUnauthorized, web.Fail("登录失败,用户名或密码错误!"))
		}
	}
}

声明接口,并加入Server中

// routes.go
func GetNoAuthRoutes(serverCtx *svc.ServiceContext) []rest.Route {
	return []rest.Route{
		{
			Method:  http.MethodPost,
			Path:    "/token/",
			Handler: TempUpLoginHandler(serverCtx),
		},
	}
}

// root_routes.go
server.AddRoutes(login.GetNoAuthRoutes(serverCtx), rest.WithPrefix("/login"))

编写一个无权访问回调函数

编写一个无权访问回调函数 UnauthorizedCallback,处理无权访问时,接口返回值

package web

import (
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/rest/httpx"
	"net/http"
)

// UnauthorizedCallback 无权访问回调
func UnauthorizedCallback(w http.ResponseWriter, r *http.Request, err error) {
	if err != nil {
		logx.Errorf("authentication error: %v", err)
	}
	httpx.OkJson(w, Fail("authentication error"))
}

创建Server时候,加入回调函数

server = rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(web.UnauthorizedCallback))

代码位置

在这里插入图片描述

测试

请求头无token
在这里插入图片描述
登录操作
在这里插入图片描述
携带token访问
在这里插入图片描述

cobra

cobra是一种创建强大的现代CLI应用程序的库。cobra用于许多GO项目,如Kubernetes,Hugo和Github Cli等。cobra遵循commands, arguments & flags结构。其中commands代表行为,arguments代表数值,flags代表对行为的改变。

引入依赖

本项目版本:v1.7.0

go get -u github.com/spf13/cobra@latest

编写root根命令

root命令是所有子命令的根

var rootCmd = &cobra.Command{
	// 使用提示:Use、Short、Long
	Use:   "tbox",
	Short: "treasure box",
	Long:  "treasure box platform",
	//命令遇到错误时不显示使用方法
	SilenceUsage: false,
	Args: func(cmd *cobra.Command, args []string) error {
		//if len(args) < 1 {
		//	return errors.New("至少需要一个参数")
		//}
		return nil
	},
	// 命令运行之前会回调
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		return nil
	},
	// 命令运行之前会回调
	PreRun: func(cmd *cobra.Command, args []string) {
	},
	// 命令运行的逻辑
	Run: func(cmd *cobra.Command, args []string) {
		tip()
	},
}
// 打印简单的提示信息
func tip() {
	fmt.Println("欢迎使用tbox平台系统, -h 参数查看帮助文档")
}

func initCmd() {
	// 添加版本控制的子命令
	rootCmd.AddCommand(newVersionCommand())
	// 添加start服务的子命令
	rootCmd.AddCommand(newRunCommand())
}

// main方法里调用,触发程序执行
func Execute() {
	// 初始化子命令
	initCmd()
	// 执行命令
	if err := rootCmd.Execute(); err != nil {
		logx.Errorf("Execute error:%v\n", err)
	}
}

编写版本信息version子命令

package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/zeromicro/go-zero/core/color"
)

func newVersionCommand() *cobra.Command {
	return &cobra.Command{
		Use:     "version",
		Short:   "version info",
		Long:    "View current version!",
		// 别名
		Aliases: []string{"V", "v"},
		Run: func(cmd *cobra.Command, args []string) {
			// 打印版本信息,颜色包裹实现彩色展示
			fmt.Println(color.WithColor("1.0.0", color.FgGreen))
		},
	}
}

编写启动start子命令

package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/zachary/tbox/internal/config"
	"github.com/zachary/tbox/internal/svc"
	"github.com/zachary/tbox/internal/util"
	"github.com/zachary/tbox/internal/web"
	"github.com/zachary/tbox/internal/web/handler"
	"github.com/zeromicro/go-zero/core/color"
	"github.com/zeromicro/go-zero/core/conf"
	"github.com/zeromicro/go-zero/rest"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func newRunCommand() *cobra.Command {
	var server *rest.Server
	runC := &cobra.Command{
		Use:     "start",
		Short:   "Run server",
		Long:    "Start http server!",
		// 给出一个使用例子
		Example: "tbox start -f etc/tbox-api.yaml",
		PreRun: func(cmd *cobra.Command, args []string) {
			// 运行之前加载配置等信息
			server = preRun(cmd)
		},
		Run: func(cmd *cobra.Command, args []string) {
			// 运行服务
			runServer(server)
		},
	}
	// 指定运行参数:-f 指定配置文件
	runC.Flags().StringP("file", "f", "etc/tbox-api.yaml", "config path")
	return runC
}

func preRun(cmd *cobra.Command) (server *rest.Server) {
	var c config.Config
	configFile := cmd.Flag("file").Value.String()
	fmt.Println(color.WithColor("loading config file:", color.FgGreen), color.WithColor(configFile, color.FgMagenta))
	conf.MustLoad(configFile, &c)
	server = rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(web.UnauthorizedCallback))
	ctx := svc.NewServiceContext(c)
	handler.RegisterHandlers(server, ctx)
	return
}

func runServer(server *rest.Server) {
	c := svc.GetServiceContext().Config
	fmt.Printf(color.WithColor("Starting server at %s:%d...\n", color.FgGreen), c.Host, c.Port)
	// 等待中断信号以优雅地关闭服务器
	// kill (no param) default send syscanll.SIGTERM
	// kill -2 is syscall.SIGINT
	// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	//开启新的goroutine启动
	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println(color.WithColor("Server start failed:", color.FgRed), err)
				time.Sleep(time.Millisecond * 200)
				quit <- syscall.SIGTERM
			}
		}()
		server.Start()
	}()
	time.Sleep(time.Millisecond * 500)
	fmt.Println(color.WithColor("Server run at:", color.FgGreen))
	fmt.Printf("-  %s:   http://localhost:%d/ \r\n", color.WithColor("Local", color.BgGreen), c.Port)
	fmt.Printf("-  %s: http://%s:%d/ \r\n", color.WithColor("Network", color.BgGreen), util.GetLocalHost(), c.Port)
	fmt.Printf("%s Enter Control + C Shutdown Server \r\n", util.NowDateTimeStr())

	<-quit
	fmt.Printf("%s Shutdown Server ... \r\n", util.NowDateTimeStr())
	log.Println("Server exiting")
	server.Stop()
}

使用测试

编译项目

go build .

测试命令

PS D:\dev\GolandProjects\treasure-box\treasure-box> .\tbox.exe
欢迎使用tbox平台系统, -h 参数查看帮助文档

# 查看-h信息
 PS D:\dev\GolandProjects\treasure-box\treasure-box> .\tbox.exe -h
treasure box platform

Usage:
  tbox [flags]
  tbox [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  start       Run server
  version     version info

Flags:
  -h, --help   help for tbox

Use "tbox [command] --help" for more information about a command.

# 查看版本信息
PS D:\dev\GolandProjects\treasure-box\treasure-box> .\tbox.exe version
1.0.0

#查看start帮助文档
PS D:\dev\GolandProjects\treasure-box\treasure-box> .\tbox start -h   
Start http server!

Usage:
  tbox start [flags]

Examples:
tbox start -f etc/tbox-api.yaml

Flags:
  -f, --file string   config path (default "etc/tbox-api.yaml")
  -h, --help          help for start

# 启动服务
PS D:\dev\GolandProjects\treasure-box\treasure-box> .\tbox start   
loading config file: etc/tbox-api.yaml
Starting server at 0.0.0.0:8888...
Server run at:
-  Local:   http://localhost:8888/ 
-  Network: http://30.236.0.84:8888/ 
2023-07-24 13:12:13 Enter Control + C Shutdown Server

代码位置

在这里插入图片描述

源码仓库地址 源码 https://gitee.com/li_zheng/treasure-box

下一篇

  1. 引入redis,分布式缓存中间件
  2. 引入gorm,持久化层框架

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/788932.html

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

相关文章

一起学SF框架系列5.8-spring-Beans-注解bean解析4-bean解析

前面三节主要讲了如何加载注解Bean的BeanDefinition&#xff0c;执行环节是在DefaultBeanDefinitionDocumentReader.parseBeanDefinitions中用BeanDefinitionParserDelegate.parseCustomElement(ele)加载的&#xff0c;实际上没对注解真正进行解析。本节主要讲述注解bean如何被…

性能测试过程有哪些?都要做什么

13.1 性能测试过程概述 13.2 性能测试设计 需要关注的问题&#xff1a;事务需求、技术需求、系统要求、团队要求 分析从五个方面分析&#xff1a;需求调研、事务模型、场景模型、数据设计、环境设计 13.2.1 需求调研 ① 测试系统预研&#xff1a;系统相关知识、系统目的、…

MinIO在Linux环境下单机安装部署

1、MinIO是什么&#xff1f; MinIO 是一个基于 Go语言实现的高性能对象存储。它采用AGPL&#xff08;GNU Affero General Public License&#xff09; 开源协议并兼容 S3 协议。 官网地址&#xff1a;https://min.io/ github地址&#xff1a;https://github.com/minio/minio …

C 程序 运算符

文章目录 1、算术运算符2、关系运算符3、逻辑运算符4、位运算符5、赋值运算符6、杂项运算符 ↦ sizeof & 三元7、运算符优先级 1、算术运算符 #include <stdio.h>int main() {int a 21;int b 10;int c ;c a b;printf("Line 1 - c 的值是 %d\n", c );c …

Jenkins从配置到实战(一) - 实现C/C++项目自动化构建

前言 本文章主要介绍了&#xff0c;如何去安装和部署Jenkins&#xff0c;并实现自动拉取项目代码&#xff0c;自动化编译流程。 网站 官网中文网站 下载安装 可以下载这个 安装jenkins前先安装java yum search java|grep jdkyum install java-1.8.0-openjdk 安装jenkins j…

我是如何做性能测试 - 文档收集并深入学习

目录 前言&#xff1a; 1. 架构设计说明书 2. 需求说明文档/需求规格说明书 3. 接口设计文档 4. 接口详细设计文档 5. 数据库设计文档 前言&#xff1a; 性能测试是软件开发过程中的一个重要方面&#xff0c;它旨在评估软件在不同负载和压力条件下的性能表现。性能测试需…

Android10 调用相机 ( 涉及读写文件 ) 闪退

背景 按照 << 第一行Android代码>>写了一下调用摄像头和相册的案例, 页面是下面截图的样子, 拍照或者从相册选择图片后, 在下方的 imageView 里将图片显示出来. 点击 “从相册选择照片” 按钮的时候功能是正常的, 点击 “拍照” 按钮的时候会闪退并报告相机异常.…

Java训练三

一、数独 将1~9的数字放入一个3x3的数组中国&#xff0c;判断数组每行每列以及每个对角线的值相加是否都相同。 package haha; public class helloworld{public static void main(String[] args) {int arr[][] {{1,2,3},{4,5,6},{7,8,9}};//每行int arr1[]new int[3];for(int…

【uniapp学习之】uni-forms必填项校验

代码块 <uni-forms ref"baseForm" :modelValue"baseFormData" label-widthauto :rules"rules"><uni-forms-item label"企业名称" required name"principalName"><uni-easyinput v-model"baseFormData.…

数组中重复的数据(力扣)思维 JAVA

给你一个长度为 n 的整数数组 nums &#xff0c;其中 nums 的所有整数都在范围 [1, n] 内&#xff0c;且每个整数出现 一次 或 两次。 请你找出所有出现 两次 的整数&#xff0c;并以数组形式返回。 你必须设计并实现一个时间复杂度为 O(n) 且仅使用常量额外空间的算法解决此问…

QT实现用户登录注册功能

本文实例为大家分享了QT实现用户登录注册的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 1、login.h ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #ifndef LOGIN_H #define LOGIN_H #include <QWidget> namespace Ui { c…

git pull无效,显示 * branch master -> FETCH_HEADAlready up to date. pull无效解决方法

报错情况 本地文件夹中删除文件后&#xff0c;git pull无效。显示如下&#xff1a; **** MINGW64 ~/****/haha (master) $ git pull origin master From https://gitee.com/****/haha* branch master -> FETCH_HEAD Already up to date.解决 方法一 命令…

C++ —— STL容器【vector】模拟实现

本章代码gitee仓库&#xff1a;vector模拟实现、vector源码 文章目录 &#x1f608;0. 搭个框架&#x1f604;1. 成员&#x1f47b;2. 构造函数 & 析构函数 & 拷贝构造&#x1f63a;3. 迭代器&#x1f649;4. operator[] & operator&#x1f916;5. 容器的容量 &am…

Vue mixin 混入

可以复用的组件&#xff0c;我们一般会抽离&#xff0c;写成公共的模块。 可以复用的方法&#xff0c;我们一般会抽离&#xff0c;写成公共的函数。 那么 在 Vue 中&#xff0c;如果 某几个组件实例 VueComponent 中、或者 整个 Vue 项目中 都存在相同的配置&#xff0c;那就…

jmeter-断言

断言作用&#xff1a;让脚本自动化执行过程中&#xff0c;能够自动判定执行结果是否正确&#xff0c;需要添加断言 响应断言 添加方式&#xff1a;测试计划–》线程组–》HTTP请求–》(右键添加)断言–》响应断言 案例 请求:https://www.baidu.com 检查&#xff1a;让程序检查…

13.4 【Linux】使用者身份切换

使用一般帐号&#xff1a;系统平日操作的好习惯 尽量以一般身份使用者来操作Linux的日常作业。等到需要设置系统环境时&#xff0c; 才变换身份成为 root 来进行系统管理&#xff0c;相对比较安全。避免作错一些严重的指令&#xff0c;例如恐怖的“ rm -rf / ”。 用较低权限…

OpenTDF数据加密引擎

OpenTDF是Virtru公司的开源项目。 Virtru基于OpenTDF开发了用于google Workspace和Microsoft 365的相关数据安全产品。 简介 virtru公司基于opentdf开发挺多产品的,都是数据安全类产品。 能把opentdf开源,已经非常不容易了。 opentdf的代码看起来还是比较整齐和成熟的。…

flink写入到kafka 大坑解析。

1.kafka能不能发送null消息&#xff1f; 能&#xff01; 2 flink能不能发送null消息到kafka&#xff1f; 不能&#xff01; public static void main(String[] args) throws Exception {StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(…

mysql 第九章

目录 1.mha 搭建 2.总结 1.mha 搭建 主从同步&#xff1a; 安装 mha 软件&#xff1a; mha 模拟 vip 飘移、master 切换&#xff1a; 2.总结 mha 是一套优秀的 mysql 高可用环境下故障切换和主从复制的软件。mha 解决 mysql 单点的问题。mysql 故障切换过程中&#xff0c;mh…

Linux 学习记录57(ARM篇)

Linux 学习记录57(ARM篇) 本文目录 Linux 学习记录57(ARM篇)一、外部中断1. 概念2. 流程图框 二、相关寄存器1. GIC CPU Interface (GICC)2. GIC distributor (GICD)3. EXTI registers 三、EXTI 寄存器1. 概述2. 内部框图3. 寄存器功能描述4. EXTI选择框图5. EXTI_EXTICR1 &…