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

golang 实现带令牌限流的JWT demo

demo里提供了三个接口,认证取token,刷新token,获取信息,token过期前也会在header里写上新token(便于客户端更换)

package mainimport ("fmt""net/http""sync""time""github.com/gin-gonic/gin""github.com/golang-jwt/jwt/v5"
)const (TOKEN_SECRET_KEY   = "secret" // 密钥TOKEN_EXPIRE_TIME  = 2 * time.Hour // 2小时过期TOKEN_REFRESH_TIME = 10 * time.Minute // 接近过期时会在header里面加上新token,客户端可以识别也可以自行拉取新
)var tb *TokenBucketfunc init() {tb = NewTokenBucket(100, 5000)
}func main() {// 创建一个Gin引擎r := gin.Default()// 限流r.Use(rateMiddWare(tb))// 登录接口r.POST("/login", loginHandler)// 受保护接口v1 := r.Group("/v1").Use(authReqMiddWare()){v1.GET("/user", userHandler)v1.GET("/refresh-token", refreshTokenHandler)}// 监听并在8080端口上启动服务r.Run(":8080")
}/*** 登陆* curl 127.0.0.1:8080/login -X POST* return {"token":"eyJhbGciO..."}*/
func loginHandler(c *gin.Context) {// TODO 验证账户密码// account + passwd 需要从db中拉取信息校验// 获取用户信息userId := "123"userName := "test 123"// 签名JWTtokenString, err := generateJWTToken(userId, userName)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to generate token"})return}// 返回JWT给客户端c.JSON(http.StatusOK, gin.H{"token": tokenString})
}/*** 用户信息* curl 127.0.0.1:8080/v1/user -H "token:eyJhbGciO..."* return {"UserId":"123","UserName":"test 123","exp":1694741333,"nbf":1694734133,"iat":1694734133}*/
func userHandler(c *gin.Context) {claims, bool := c.Get("claims")if bool {// TODO 其他用户信息可以用UID查 缓存 和 数据库// findbyId()c.JSON(http.StatusOK, claims)return}c.JSON(http.StatusOK, gin.H{"message": "not found"})
}/*** 刷新token* curl 127.0.0.1:8080/v1/refresh-token -H "token:eyJhbGciO..."* return {"token":"eyJhbGciO..."}*/func refreshTokenHandler(c *gin.Context) {claims, bool := c.Get("claims")if !bool {c.JSON(http.StatusOK, gin.H{"message": "not found claims"})return}fmt.Println(claims)val, ok := claims.(*jwtClaims)if !ok {c.JSON(http.StatusOK, gin.H{"message": "not found"})return}tokenString, err := generateJWTToken(val.UserId, val.UserName)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to generate token"})return}c.JSON(http.StatusOK, gin.H{"token": tokenString})return
}//jwt
type jwtClaims struct {UserId               stringUserName             stringjwt.RegisteredClaims // jwt中标准格式
}/*** 校验token* 如果想从服务端控制发出的token,可以通过redis标记也能达到让指定token提前过期的目的*/ 
func authReqMiddWare() gin.HandlerFunc {return func(c *gin.Context) {// 读取TOKENtokenStr := c.GetHeader("token")if tokenStr == "" {c.JSON(http.StatusForbidden, gin.H{"message": "Token not exist"})c.Abort()return}// 解析tokentoken, err := jwt.ParseWithClaims(tokenStr, &jwtClaims{}, func(token *jwt.Token) (interface{}, error) {return []byte(TOKEN_SECRET_KEY), nil})if err != nil {c.JSON(http.StatusForbidden, gin.H{"message": err.Error()})c.Abort()return}claims, ok := token.Claims.(*jwtClaims)// 这里默认会检查ExpiresAt是否过期if ok && token.Valid { now := time.Now()// 检查过期时间,对快要过期的添加http header `refresh-token`if t := claims.ExpiresAt.Time.Add(-TOKEN_REFRESH_TIME); t.Before(now) {tokenString, err := generateJWTToken(claims.UserId, claims.UserName)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to generate token"})c.Abort()return}c.Header("refresh-token", tokenString) //}c.Set("claims", claims)}}
}// 生成JWT token
func generateJWTToken(userId, userName string) (string, error) {now := time.Now()claims := jwtClaims{UserId:   userId,UserName: userName,RegisteredClaims: jwt.RegisteredClaims{ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(TOKEN_EXPIRE_TIME)}, // 过期时间IssuedAt:  jwt.NewNumericDate(now), // 签发时间NotBefore: jwt.NewNumericDate(now), // 生效时间},}token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)return token.SignedString([]byte(TOKEN_SECRET_KEY))
}// 限流
func rateMiddWare(tb *TokenBucket) gin.HandlerFunc {return func(c *gin.Context) {if !tb.AllowRequest() {c.JSON(http.StatusTooManyRequests, gin.H{"message": http.StatusText(http.StatusTooManyRequests)})c.Abort()return}}
}// 令牌
type TokenBucket struct {cap      int       // 桶容量rate     float64   // 每秒生产个数tokenNum int       // 当前计数lastTime time.Time // 上一个产生时间mu       sync.Mutex
}func NewTokenBucket(cap int, rate float64) *TokenBucket {return &TokenBucket{cap:      cap,rate:     rate,tokenNum: cap,lastTime: time.Now(),}
}// 拿令牌
func (tb *TokenBucket) AllowRequest() bool {tb.mu.Lock()defer tb.mu.Unlock()now := time.Now()second := now.Sub(tb.lastTime).Seconds() // 计算经过多少秒newTokens := int(second * tb.rate)       // 计算产生的令牌数量if newTokens > 0 {tb.tokenNum = tb.tokenNum + newTokensif tb.tokenNum > tb.cap { // 不能超过容量tb.tokenNum = tb.cap}tb.lastTime = now}if tb.tokenNum > 0 {tb.tokenNum--return true}return false
}

相关文章:

golang 实现带令牌限流的JWT demo

demo里提供了三个接口,认证取token,刷新token,获取信息,token过期前也会在header里写上新token(便于客户端更换) package mainimport ("fmt""net/http""sync""time&qu…...

【web开发】9、Django(4)ajax请求

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、Ajax是什么?二、使用步骤二、订单管理 提示:以下是本篇文章正文内容,下面案例可供参考 一、Ajax是什么? Ajax&…...

消息队列中,如何保证消息的顺序性?

本文选自:advanced-java 作者:yanglbme 问:如何保证消息的顺序性? 面试官心理分析 其实这个也是用 MQ 的时候必问的话题,第一看看你了不了解顺序这个事儿?第二看看你有没有办法保证消息是有顺序的&#xf…...

Shell别名的使用方法及管理技巧

文章目录 1. 引言1.1 概述1.2 目的1.3 适用范围 2. Shell和别名2.1 Shell简介2.2 别名的作用2.3 别名的语法 3. 创建别名3.1 临时别名3.2 永久别名 4. 别名的应用4.1 简化命令4.2 自定义命令4.3 提高工作效率 5. 管理别名5.1 查看别名5.2 修改别名5.3 删除别名 6. 实例演示6.1 …...

C/C++选择题好题分享

...

kafka副本机制

目录 前言 副本定义 副本角色 In-sync Replicas(ISR) 参考资料 前言 现在的很多的分布式系统都支持副本的机制,比如Mysql就有副本的机制,一般使用副本有如下特性和好处。 提供数据冗余。即使系统部分组件失效,系…...

服务注册发现_actuator微服务信息完善

SpringCloud体系里的&#xff0c;服务实体向eureka注册时&#xff0c;注册名默认是IP名:应用名:应用端口名。 问题&#xff1a; 自定义服务在Eureka上的实例名怎么弄呢 在服务提供者pom中配置Actuator依赖 <!-- actuator监控信息完善 --> <dependency><groupId…...

常见列表字典排序

一、列表排序 demoList [1, 3, 2, 4, 9 ,7]res sorted(demoList) # 默认升序# 降序 # res sorted(demoList, reverseTrue)print(res)二、字典排序 demoDict {"篮球": 5, "排球": 9, "网球": 6, "足球": 3}# sorted排序 res so…...

【Acwing1027】方格取数(动态规划)题解

题目描述 思路分析 错误思路&#xff1a; 贪心法&#xff0c;先走一次求出最大值&#xff0c;把走过的路上面的数值清零&#xff0c;然后用同样的方法再走一遍求最大值&#xff0c;然后让这两个最大值相加就是最后的结果。 很多人在看到这个题目的时候会有上面的思路&#x…...

合并区间:解决区间重叠问题的高效算法

合并区间&#xff1a;解决区间重叠问题的高效算法 leetcode 56. 合并区间 合并区间是一个常见的编程问题&#xff0c;通常涉及到一组区间&#xff0c;你需要将重叠的区间合并成更大的区间。这篇博客将介绍这个问题的背景&#xff0c;然后解释一个高效的解决方案&#xff0c;同…...

万字总结HTML超文本标记语言

一、前言:什么是网页? 网站是指在因特网上根据一定的规则,使用 HTML 等制作的用于展示特定内容相关的网页集合。网页是网站中的一“页”,通常是 HTML 格式的文件,它要通过浏览器来阅读。 网页是构成网站的基本元素,它通常由图片、链接、文字、声音、视频等元素组成。通常…...

Java线程池是如何保证核心线程不被销毁的

来源: Java线程池是如何保证核心线程不被销毁的_朝 花 拾 夕的博客-CSDN博客 对于Java中 Thread 对象&#xff0c;同一个线程对象调用 start 方法后&#xff0c;会在执行完run 后走向终止&#xff08;TERMINATED&#xff09;状态&#xff0c;也就是说一个线程对象是不可以通过多…...

新课程标准培养学生“高考物理关键能力”的实践研究课题文献综述

目录 一、高考物理能力的要求与评估标准 二、高考物理关键能力的定义与内涵...

急救车工业路由器应用提升急救效率:车联网、数据采集与远程诊疗

急救车作为医院里医疗急救过程中的重要组成部分&#xff0c;在智慧医疗物联网领域中急救车应用4G工业路由器实现网络部署与数据采集&#xff0c;通过工业4G路由器能够实时采集到病患的生理数据、救护现场音频与视频、GPS定位以及车辆运行状态等重要信息。这些数据将被传输到医疗…...

【操作系统】聊聊CPU上下文切换实操

如何查看系统的上下文切换情况 上一篇文章我们说了过多的上下文切换&#xff0c;会把CPU时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上&#xff0c;那么当出现系统的上下文切换过多的时候&#xff0c;我们如果通过监控指标查看呢。 vmstat 是一个常用的系统性能…...

【java】【SpringBoot】【四】原理篇 bean、starter、核心原理

目录 一、自动配置 1、bean加载方式&#xff08;复习&#xff09; 1.1 加载方式-xml方式生命bean 1.2 加载方式-xml注解方式声明bean 1.3 注解方式声明配置类 1.4 FactoryBean 1.5 proxyBeanMethod属性 1.6 使用Import注解导入 1.7 使用上下文对象在容器初始化完毕后注…...

【精品资源】Java毕业设计攻略:从选题到答辩,一站式指南

导读&#xff1a; Java毕业设计是计算机科学与技术专业学生展示其编程能力、问题解决能力和创新思维的重要环节。这篇博客将为您提供一站式的Java毕业设计攻略&#xff0c;帮助您从选题到答辩&#xff0c;顺利完成毕业设计。 一、选题阶段 寻找灵感&#xff1a; 探讨热门技术如…...

文件高效批量重命名,轻松重命名不同类型的文件名并隐藏编号

你是否曾经因为文件名混乱而感到困扰&#xff1f;你是否希望有一种方法可以快速、简单地管理你的文件名&#xff1f;如果你的答案是肯定的&#xff0c;那么我们的产品——文件重命名工具&#xff0c;将是你的完美解决方案&#xff01; 首先我们要进入文件批量改名高手主页面&a…...

接口的定义与实现

一个c&#xff0c;代表类&#xff08;class&#xff09;。 一个c再加上两竖线&#xff0c;代表抽象类。 一个i&#xff0c;代表接口&#xff08;interface&#xff09;。 package com.mypackage.oop.demo12;//接口都需要有一个实现类 public interface UserService {//接口中定…...

浅谈低压绝缘监测及定位系统在海上石油平台的研究与应用

安科瑞 华楠 摘要&#xff1a;海上石油平台低压系统与陆地电力系统有很大区别&#xff0c;其属于中性点绝缘系统&#xff0c;在出现单相接地故障时&#xff0c;系统允许带故障正常运行2 h&#xff0c;保证海上重要电气设备不会立即关停。现以渤海某海上平台为例&#xff0c;其…...

Java项目:SSM的食堂点餐系统

作者主页&#xff1a;Java毕设网 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 一、相关文档 系统中的核心用户是系统管理员&#xff0c;管理员登录后&#xff0c;通过管理员菜单来管理后台系统。主要功能有&#xff1a;个人中心、用户管理…...

Linux桌面环境中应用程序无法启动图形交互界面

现象&#xff1a; 点击永中office或者金山office快捷图标无法启动对应的程序。 从命令行执行对应的程序则提示 按照提示安装组件 再次执行命令行程序 原因探析&#xff1a; /opt/Yozosoft/Yozo_Office/Yozo_Writer.bin: error while loading shared libraries: libgdk-x11-2.0.…...

jupyter notebook进不去指定目录怎么办?

首先激活你要使用的虚拟环境 刚开始是现在 (base) C:\Users\lenovo>目录下 直接输入你想进入的盘 (base) C:\Users\lenovo>e:此时再cd (base) C:\Users\lenovo>cd E:\tim\learn_pytorch 就可以进入了 安装3.4.1.15问题 已经有了最新python版本的虚拟环境&#…...

MySQL 高级(进阶) SQL 语句(二) -----存储过程

目录 1 存储过程 1.1 创建存储过程​ 1.2 调用存储过程 1.3 查看存储过程 1.4 存储过程的参数 1.5 修改存储过程 1.6 删除存储过程 2 条件语句 3 循环语句 1 存储过程 存储过程是一组为了完成特定功能的SQL语句集合。 存储过程在使用过程中是将常用或者复杂的工作预…...

机器学习第十三课--主成分分析PCA

一.高维数据 除了图片、文本数据&#xff0c;我们在实际工作中也会面临更多高维的数据。比如在评分卡模型构建过程中&#xff0c;我们通常会试着衍生出很多的特征&#xff0c;最后就得到上千维、甚至上完维特征;在广告点击率预测应用中&#xff0c;拥有几个亿特征也是常见的事…...

钉钉stream机器人-实操详细教程

支持事件订阅、机器人收消息、卡片回调等功能 优点&#xff1a; 配置简单&#xff0c;不依赖也不需要暴露公网IP&#xff0c;无需向公网开放端口 github官方链接&#xff1a;GitHub - open-dingtalk/dingtalk-stream-sdk-python: Python SDK for DingTalk Stream Mode API, Co…...

设计模式:访问者模式(C++实现)

访问者模式通过将对元素的操作与元素本身分离&#xff0c;使得可以在不修改元素类的情况下定义新的操作。 #include <iostream> #include <vector> #include <algorithm>// 前向声明 class ConcreteElementA; class ConcreteElementB;// 访问者接口 class V…...

Pygame中Sprite的使用方法6-6

4 重新绘制界面 每次碰撞发生后&#xff0c;程序界面需要重新绘制&#xff0c;代码如下所示。 screen.fill(WHITE) all_sprites_list.draw(screen) pygame.display.flip() 其中&#xff0c;screen表示程序的整个界面&#xff0c;将其绘制为白色背景&#xff1b;之后通过all_…...

react多条件查询

1、声明一个filter常量 2.filter接受&#xff08;condition,data&#xff09;两个参数 3、调用data里面的filter进行筛选 4、任意一个item当筛选条件 5、使用object.key获取对象所有key 6、对每个key使用Array.prototype.every&#xff08;&#xff09;方法判断是否满足条…...

2023/09/17

文章目录 1. vscode展开所有代码快捷键ctrl k j2. git删除所有stash或指定stash git stash drop [可选stash名]3. vue在函数默认参数后增加新参数4. git push 添加“-u”参数5. vscode快捷输入符号$的使用6. WebGL之什么是GLB&GLTF文件&#xff1f;7. WebGL之什么是HDR&a…...

青岛 公司 网站建设价格/百度金融

C从0到1全系列教程 整数默认是十进制&#xff0c;一个表示十进制的数字不需要任何特殊的格式。 1、二进制 二进制由 0 和 1 两个数字组成&#xff0c;书写时必须以0b或0B&#xff08;不区分大小写&#xff09;开头。 以下是合法的二进制&#xff1a; int a 0b101; // …...

制作网页心得体会/网站的seo

1953年5月29日&#xff0c;一位攀登者和他的向导千辛万苦来到了世界之颠的珠穆朗玛峰&#xff0c;在此之前&#xff0c;世界上还没有人到过这样的高度。 世界之颠与他们只有短短的两米&#xff0c;其中一人中要向前跨几步就可以成为这 个世界上的唯一&#xff0c;而这几步对于谁…...

校园网站如何管理/seo双标题软件

http://hi.baidu.com/mathspace/blog/item/3049ebd3b6edf4d8a8ec9a3e.html一、日期时间1、char *asctime(const struct tm *timeptr)将timeptr所指的tm结构中的信息转换为真实世界所使用的时间日期表示方法&#xff0c;然后将结果以字符串形态返回2、char *ctime(const time_t …...

搞笑网站全站源码/余姚关键词优化公司

1.部分反射API是泛型1.1获取反射API的泛型部分反射API是泛型&#xff0c;如Class是泛型//如果使用Class&#xff0c;不带泛型&#xff0c;出现compile warning编译警告Class clazz1 String.class;String str1 (String) clazz1.newInstance();//改写为带泛型的Class&#xff0…...

wordpress cdn 非插件/什么是广告营销

顺序查找算法 1. 算法描述 顺序比较即可。 2. 平均查找长度 (n1)/2, 其中n为表长。 3. 算法实现 省略 4. 优化思想 根据经验&#xff0c;目前被查到越多的元素&#xff0c;将来可能被查到的可能性也越大。所以可以考虑&#xff0c;每次查找到一个元素后&#…...

网络规划设计师第二版/东莞seo项目优化方法

检查docker的版本&#xff0c;这样可以用来确认docker服务在运行并可通过客户端链接。 提示&#xff1a; 可以通过在终端输入docker命令来查看所有的参数。 # docker -v 转载于:https://www.cnblogs.com/amwuau/p/6530955.html...