【AI图像生成网站Golang】JWT认证与令牌桶算法

news2024/11/21 14:51:04

AI图像生成网站

目录

一、项目介绍

二、雪花算法

三、JWT认证与令牌桶算法

四、项目架构

五、图床上传与图像生成API搭建

六、项目测试与调试(等待更新)


三、JWT认证与令牌桶算法

在现代后端开发中,用户认证和接口限流是确保系统安全性和性能的两大关键要素。本文将基于实际代码,介绍 JWT 认证 和 令牌桶限流算法 的原理和实现。


1. JWT认证

JWT(JSON Web Token)是一种开放标准(RFC 7519),定义了一种紧凑的、自包含的方式,用于在各方之间安全地传输信息。这些信息经过签名验证后,可以信任其真实性。它通常用于用户认证场景,流程如下:

  1. 用户登录成功后,服务器生成一个JWT并返回给客户端。
  2. 客户端每次访问受保护的接口时,将JWT放入请求头中。
  3. 服务器解析JWT验证用户身份。

一个典型的JWT由三部分组成:

  • Header:描述加密算法类型(如 HS256)。
  • Payload(有效载荷):实际数据,例如用户信息和 Token 过期时间。
  • Signature:通过密钥和 Header、Payload 签名生成,用于验证数据的完整性。

        JWT 的标准声明只包含一些通用字段(如 expiat1),但在实际应用中,我们需要存储更多的业务数据,比如用户 ID 和用户名。
        在 JWT 中,Payload 就是 Token 的核心数据部分,用来存储那些需要在两方之间传递的信息。它包含了自定义的声明(Claims),例如用户的标识(user_id)或过期时间(exp)。Payload 不会被加密,但会被签名以保证数据的完整性。我们可以自定义结构体 MyClaims来实现自定义声明:

type MyClaims struct {
	UserID   uint64 `json:"user_id"`
	Username string `json:"username"`
	jwt.StandardClaims
}

之后,我们需要生成Access TokenRefresh Token来减少用户的重复登录行为,从而在保证安全性的同时提高交互体验,具体交互过程为:

在这里插入图片描述

用户登录阶段

  1. 用户在登录页面输入用户名和密码。
  2. 服务器验证用户的身份后,生成并返回:
    • 一个短期有效的 Access Token。
    • 一个长期有效的 Refresh Token。
  3. 客户端存储 Token(通常 Access Token 存在内存中,Refresh Token 存在安全存储区)。

生成 Access TokenRefresh Token 的函数如下:

// 定义Secret 用于加密的字符串
var mySecret = []byte("aidraw")

func GenToken(userID uint64, username string) (aToken, rToken string, err error) {
	c := MyClaims{
		UserID:   userID,
		Username: username,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(AccessTokenExpireDuration).Unix(),
			Issuer:    "aidraw",
		},
	}
	aToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, c).SignedString(mySecret)

	rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
		ExpiresAt: time.Now().Add(RefreshTokenExpireDuration).Unix(),
		Issuer:    "aidraw",
	}).SignedString(mySecret)

	return
}

与网站交互阶段

  1. 初始请求:客户端将 Access Token 添加到每个请求的 HTTP 头部(Authorization: Bearer <Access Token>)。服务器解析 Token 并验证用户身份。

解析Token的代码为:

func keyFunc(_ *jwt.Token) (i interface{}, err error) {
	return mySecret, nil
}

// ParseToken 解析Token.
func ParseToken(tokenString string) (claims *MyClaims, err error) {
	claims = new(MyClaims)
	token, err := jwt.ParseWithClaims(tokenString, claims, keyFunc)
	if err != nil {
		return
	}
	if !token.Valid {
		err = errors.New("invalid token")
	}
	return
}

  1. Access Token 过期:当 Access Token 失效时,客户端会用 Refresh Token 请求新的 Access Token。

刷新Token的代码为:

func RefreshToken(aToken, rToken string) (newAToken, newRToken string, err error) {
	// 验证 Refresh Token 是否有效
	if _, err = jwt.Parse(rToken, keyFunc); err != nil {
		return
	}

	// 解析 Access Token 提取用户信息
	var claims MyClaims
	_, err = jwt.ParseWithClaims(aToken, &claims, keyFunc)
	v, _ := err.(*jwt.ValidationError)

	// 如果 Access Token 是过期错误,生成新的 Token
	if v.Errors == jwt.ValidationErrorExpired {
		return GenToken(claims.UserID, claims.Username)
	}
	return
}

  1. 刷新 Token 过程
    1. 客户端发送 Refresh Token 给 /refresh_token API。
    2. 服务器验证 Refresh Token 是否有效。
    3. 如果 Refresh Token 合法且未过期,生成新的 Access Token 和 新的 Refresh Token。

2. 基于 JWT 的认证中间件

为了在路由处理函数中提取用户信息,我们需要实现一个基于 JWT 的 Gin 中间件:

  1. 从请求头的 Authorization 字段提取 Token。
  2. 验证 Token 的合法性。
  3. 将解析出的用户信息保存到上下文中,供后续的路由函数使用。
    代码如下:
package middlewares

import (
	"backend/controller"
	"backend/pkg/jwt"
	"fmt"
	"strings"
	"github.com/gin-gonic/gin"
)

// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
	return func(c *gin.Context) {
		// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
		// 这里假设Token放在Header的Authorization中,并使用Bearer开头
		// 这里的具体实现方式要依据你的实际业务情况决定
		authHeader := c.Request.Header.Get("Authorization")
		if authHeader == "" {
			controller.ResponseErrorWithMsg(c, controller.CodeInvalidToken, "请求头缺少Auth Token")
			c.Abort()
			return
		}
		// 按空格分割
		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2) {
			controller.ResponseErrorWithMsg(c, controller.CodeInvalidToken, "Token格式不对")
			c.Abort()
			return
		}
		// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
		mc, err := jwt.ParseToken(parts[1])
		if err != nil {
			fmt.Println(err)
			controller.ResponseError(c, controller.CodeInvalidToken)
			c.Abort()
			return
		}
		// 将当前请求的userID信息保存到请求的上下文c上
		c.Set(controller.ContextUserIDKey, mc.UserID)
		c.Next() // 后续的处理函数可以用过c.Get(ContextUserIDKey)来获取当前请求的用户信息
	}
}


3. 令牌桶限流算法

在现代 Web 开发中,流量控制是确保系统稳定性的重要手段之一。令牌桶算法(Token Bucket Algorithm)是一种广泛使用的限流算法,可以高效处理突发流量。其核心思想如下:

  • 固定速率发放令牌:按照指定的时间间隔,将令牌加入桶中。
  • 允许突发流量:桶有一个固定的容量,当令牌数量达到容量时,新的令牌会被丢弃。
  • 请求消耗令牌:每次请求需要消耗一定数量的令牌,如果桶中没有足够的令牌,请求将被拒绝或等待。

适用于需要控制 API 的访问频率、允许短时间内的突发请求(如秒杀活动)的场景。

令牌桶中间件实现

实现令牌桶限流中间件代码如下:

package middlewares

import (
	"github.com/gin-gonic/gin"
	"github.com/juju/ratelimit"
	"net/http"
	"time"
)

// RateLimitMiddleware 创建指定填充速率和容量大小的令牌桶
func RateLimitMiddleware(fillInterval time.Duration, cap int64) func(c *gin.Context) {
	// 创建令牌桶
	bucket := ratelimit.NewBucket(fillInterval, cap)
	return func(c *gin.Context) {
		// 检查是否能够获取令牌
		if bucket.TakeAvailable(1) == 0 {
			// 如果令牌不足,返回限流提示
			c.String(http.StatusOK, "rate limit...")
			c.Abort() // 中断请求
			return
		}
		// 如果获取到令牌,继续处理请求
		c.Next()
	}
}

参数说明:

  • fillInterval:令牌添加的时间间隔。
  • cap:桶的容量。

调用代码

本项目通过在所有路由之前设置令牌桶中间件实现了全局限流:

//初始化 gin Engine  新建一个没有任何默认中间件的路由
r := gin.New()
//设置中间件
r.Use(
	middlewares.RateLimitMiddleware(2*time.Second, 40), // 每两秒钟添加十个令牌  全局限流
)

r.LoadHTMLFiles("templates/index.html") // 加载html

  1. expiat 是 JWT 中的标准声明字段(Standard Claims)。这些字段遵循 RFC 7519 的规范,表示 Token 的时间相关信息。
            exp(Expiration Time):表示 Token 的过期时间。单位为秒,自 Unix 时间纪元(1970-01-01 00:00:00 UTC)以来的秒数。当客户端请求到达服务器时,如果当前时间大于 exp,Token 会被判定为无效。例如1699844000 表示过期时间是 2023-11-12 10:00:00 UTC。
            iat(Issued At):表示 Token 签发的时间。单位同样为秒,用于标识 Token 的创建时间。它可以用来防止 Token 重放攻击(Replay Attack)。 ↩︎

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

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

相关文章

基于Kafka2.1解读Consumer原理

文章目录 概要整体架构流程技术名词解释技术细节coordinatorfetcherclientconsumer#poll的主要流程 全局总览小结 概要 继上一篇讲Producer原理的文章过去已经一个多月了&#xff0c;今天来讲讲Consumer的原理。 其实源码早就读了部分了&#xff0c;但是最近工作比较忙&#x…

测试使用vite搭建的uni-app打包app区分开发环境和生产环境

用脚手架搭建的uniapp项目&#xff0c;打包H5和小程序可以和web端一样&#xff0c;能够通过env.dev和env.prod区分开发环境和生产环境&#xff0c;但是不知道打包成app时如何区分开发环境和生产环境&#xff0c;在此做一个测试记录。 打开package.json文件&#xff0c;在scrip…

【提效工具开发】管理Python脚本执行系统实现页面展示

Python脚本执行&#xff1a;工具管理Python脚本执行系统 背景 在现代的软件开发和测试过程中&#xff0c;自动化工具和脚本的管理变得至关重要。为了更高效地管理工具、关联文件、提取执行参数并支持动态执行Python代码&#xff0c;我们设计并实现了一套基于Django框架的工具…

Qt-常用的显示类控件

QLabel QLabel有如下核心属性&#xff1a; 关于文本格式的验证&#xff1a; 其中<b>xxx<b>&#xff0c;就是加粗的意思。 效果&#xff1a; 或者再把它改为markdown形式的&#xff1a; 在markd中&#xff0c;#就是表示一级标题&#xff0c;我们在加上##后&#x…

2024 RISC-V中国峰会 安全相关议题汇总

安全之安全(security)博客目录导读 第四届 RISC-V 中国峰会(RISC-V Summit China 2024)于8月21日至23日在杭州成功举办。此次峰会汇聚了 RISC-V 国际基金会、百余家重点企业及研究机构,约3000人线下参与,并在19日至25日间举办了超过20场同期活动,与全球开发者共同…

聊一聊Elasticsearch的索引分片的恢复机制

1、什么是索引分片的恢复&#xff1f; 所谓索引分片的恢复指的是在某些条件下&#xff0c;索引分片丢失&#xff0c;ES会把某索引的分片复制一份来得到该分片副本的过程。 2、触发分片恢复的场景有哪些&#xff1f; 分片的分配 当集群中节点的数量发生变化&#xff0c;或者配…

典型的 SOME/IP 多绑定用例总结

SOME/IP 部署中 AP SWC 不自行打开套接字连接的原因 在典型的 SOME/IP 网络协议部署场景里&#xff0c;AP SWC 不太可能自己打开套接字连接与远程服务通信&#xff0c;因为 SOME/IP 被设计为尽可能少用端口。这一需求源于低功耗 / 低资源的嵌入式 ECU&#xff0c;并行管理大量…

MySQL查询执行(八):Memory引擎

思考&#xff1a;两个group by语句都用了order bynull&#xff0c; 为什么使用内存临时表得到的语句结果里&#xff0c; 0这个值在最后一行&#xff1b; 而使用磁盘临时表得到的结果里&#xff0c; 0这个值在第一行&#xff1f; 答&#xff1a;答案对应第一小节&#xff1a;内…

canva 画图 UI 设计

起因&#xff0c; 目的: 来源: 客户需求。 目的&#xff1a; 用数据讲故事。 数据可以瞎编&#xff0c;图表一定要漂亮。 文件分享地址 读者可以在此文件的基础上&#xff0c;继续编辑。 效果图 过程: 我还是喜欢 canva. figma&#xff0c; 我用的时候&#xff0c;每每都想…

ES分词环境实战

文章目录 安装下载1.1 下载镜像1.2 单节点启动 防火墙设置异常处理【1】iptable链路中断 参考文档 参加完2024年11月软考&#xff0c;对ES的分词进行考查&#xff0c;前期有【 Docker 环境下安装部署 Elasticsearch 和 kibana】和【 Docker 环境下为 Elasticsearch 安装IK 分…

论文精读: PRB LiVSe2 Zigzag链序实验与理论计算

DOI: 10.1103/PhysRevB.108.094107 摘要节选 在具有轨道自由度的过渡金属化合物中&#xff0c;组成元素在低温下自组装形成分子的现象普遍存在。 在本研究中从实验和理论两方面讨论了钒二维三角形晶格层状LiVX2 &#xff08;X O&#xff0c; S, Se&#xff09;体系中出现的三…

修改一下达梦disql 提示符

经常用disql的有时某些信息希望提示一下&#xff0c;默认的只显示SQL> 为了方便使用&#xff0c;可以在 glogin.sql 中增加些内容。 vi $DM_HOME/bin/disql_conf/glogin.sql增加以下几行 set time on set lineshow offcol global_name new_value global_name SELECT ins…

【蓝桥杯备赛】123(前缀和的复杂应用)

5. 前缀和的复杂应用 5.1. 123&#xff08;4 星&#xff09; 5.1.1. 题目解析 这道题仍然是求一段区间的和&#xff0c;很容易能够想到前缀和找规律&#xff1a; 1------------------1 号块 1 2----------------2 号块 1 2 3--------------3 号块 1 2 3 4------------4 号…

机器学习—学习曲线

学习曲线是帮助理解学习算法如何工作的一种方法&#xff0c;作为它所拥有的经验的函数。 绘制一个符合二阶模型的学习曲线&#xff0c;多项式或二次函数&#xff0c;画出交叉验证错误Jcv&#xff0c;以及Jtrain训练错误&#xff0c;所以在这个曲线中&#xff0c;横轴将是Mtrai…

数据库基础(MySQL)

1. 数据库基础 1.1 什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a; 文件的安全性问题文件不利于数据查询和管理文件不利于存储海量数据文件在程序中控制不方便 数据库存储介质&#xff1a; 磁盘内存 为…

2024年11月HarmonyOS应用开发者基础认证全新题库

注意事项&#xff1a;切记在考试之外的设备上打开题库进行搜索&#xff0c;防止切屏三次考试自动结束&#xff0c;题目是乱序&#xff0c;每次考试&#xff0c;选项的顺序都不同 更新时间&#xff1a;2024年11月1日 这是基础认证题库&#xff0c;不是高级认证题库注意看清楚标…

静态时序分析--时序约束

目录 1.时钟约束1.1创建时钟1.2.生成时钟1.3虚拟时钟1.4 最小时钟脉宽 2.I/O延时约束2.1设置输入延时2.2设置输出延时 3.I/O环境建模约束3.1输入驱动建模3.2输出负载建模 4.时序例外4.1多周期路径设置&#xff08;multicycle path&#xff09;4.2伪路径设置&#xff08;false_p…

51单片机基础05 实时时钟-思路及代码参考2、3

目录 一、思路二 1、原理图 2、代码 二、思路三 1、原理图 2、代码 一、思路二 所有设定功能相关的操作均在矩阵键盘进行实现&#xff0c;并在定时器中扫描、计数等 1、原理图 2、代码 #include <AT89X52.h> //调用51单片机的头文件 //------------------…

【C++篇】深入剖析C++ Vector底层源码及实现机制

文章目录 须知 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&#xff1…

【代码pycharm】动手学深度学习v2-04 数据操作 + 数据预处理

数据操作 数据预处理 1.数据操作运行结果 2.数据预处理实现运行结果 第四课链接 1.数据操作 import torch # 张量的创建 x1 torch.arange(12) print(1.有12个元素的张量&#xff1a;\n,x1) print(2.张量的形状&#xff1a;\n,x1.shape) print(3.张量中元素的总数&#xff1…