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

Go 后端中双 token 的实现模板

下面是一个典型的 Go 后端双 Token 认证机制 实现模板,使用 Gin 框架 + JWT + Redis,结构清晰、可拓展,适合实战开发。


项目结构建议

/utils├── jwt.go         // Access & Refresh token 的生成和解析├── claims.go      // 从请求中提取用户信息
/middleware└── auth.go        // 中间件:校验 Access Token + 黑名单
/controller├── auth.go        // 登录、刷新、登出接口
/redis└── client.go      // Redis 黑名单管理

1. JWT 工具(utils/jwt.go

package utilsimport ("time""github.com/golang-jwt/jwt/v5"
)var accessSecret = []byte("access_secret")
var refreshSecret = []byte("refresh_secret")type CustomClaims struct {UserID uint `json:"user_id"`jwt.RegisteredClaims
}func GenerateAccessToken(userID uint) (string, error) {claims := CustomClaims{UserID: userID,RegisteredClaims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),IssuedAt:  jwt.NewNumericDate(time.Now()),},}return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(accessSecret)
}func GenerateRefreshToken(userID uint) (string, error) {claims := CustomClaims{UserID: userID,RegisteredClaims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)),IssuedAt:  jwt.NewNumericDate(time.Now()),},}return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(refreshSecret)
}func ParseToken(tokenStr string, isRefresh bool) (*CustomClaims, error) {key := accessSecretif isRefresh {key = refreshSecret}token, err := jwt.ParseWithClaims(tokenStr, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {return key, nil})if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {return claims, nil}return nil, err
}

2. 中间件验证 Access Token(middleware/auth.go

package middlewareimport ("chat/redis""chat/utils""github.com/gin-gonic/gin""net/http""strings"
)func JWTAuthMiddleware() gin.HandlerFunc {return func(c *gin.Context) {authHeader := c.GetHeader("Authorization")if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {c.JSON(http.StatusUnauthorized, gin.H{"error": "token required"})c.Abort()return}token := strings.TrimPrefix(authHeader, "Bearer ")claims, err := utils.ParseToken(token, false)if err != nil {c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})c.Abort()return}if redis.IsBlacklisted(token) {c.JSON(http.StatusUnauthorized, gin.H{"error": "token is blacklisted"})c.Abort()return}c.Set("userID", claims.UserID)c.Next()}
}

3. Redis 黑名单管理(redis/client.go

package redisimport ("context""time""github.com/redis/go-redis/v9"
)var RDB *redis.Clientfunc InitRedis() {RDB = redis.NewClient(&redis.Options{Addr: "localhost:6379",DB:   0,})
}var ctx = context.Background()func AddToBlacklist(token string, expiration time.Duration) {RDB.Set(ctx, "blacklist:"+token, "1", expiration)
}func IsBlacklisted(token string) bool {val, err := RDB.Get(ctx, "blacklist:"+token).Result()return err == nil && val == "1"
}

4. 登录、刷新、退出接口(controller/auth.go

package controllerimport ("chat/redis""chat/utils""github.com/gin-gonic/gin""net/http""time"
)func Login(c *gin.Context) {// 假设账号密码校验通过,用户ID是 123userID := uint(123)accessToken, _ := utils.GenerateAccessToken(userID)refreshToken, _ := utils.GenerateRefreshToken(userID)c.SetCookie("refresh_token", refreshToken, 7*24*3600, "/", "localhost", false, true)c.JSON(http.StatusOK, gin.H{"access_token": accessToken})
}func Refresh(c *gin.Context) {refreshToken, err := c.Cookie("refresh_token")if err != nil {c.JSON(http.StatusUnauthorized, gin.H{"error": "refresh token not found"})return}claims, err := utils.ParseToken(refreshToken, true)if err != nil {c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid refresh token"})return}newAccessToken, _ := utils.GenerateAccessToken(claims.UserID)c.JSON(http.StatusOK, gin.H{"access_token": newAccessToken})
}func Logout(c *gin.Context) {token := c.GetHeader("Authorization")if token != "" {token = token[len("Bearer "):]redis.AddToBlacklist(token, 15*time.Minute) // 过期时间与 access token 一致}c.SetCookie("refresh_token", "", -1, "/", "localhost", false, true)c.JSON(http.StatusOK, gin.H{"message": "logout success"})
}

5. 路由注册(main.go

r := gin.Default()
redis.InitRedis()auth := r.Group("/auth")
{auth.POST("/login", controller.Login)auth.POST("/refresh", controller.Refresh)auth.POST("/logout", controller.Logout)
}api := r.Group("/api", middleware.JWTAuthMiddleware())
{api.GET("/me", func(c *gin.Context) {userID := c.GetUint("userID")c.JSON(http.StatusOK, gin.H{"user_id": userID})})
}

相关文章:

Go 后端中双 token 的实现模板

下面是一个典型的 Go 后端双 Token 认证机制 实现模板,使用 Gin 框架 JWT Redis,结构清晰、可拓展,适合实战开发。 项目结构建议 /utils├── jwt.go // Access & Refresh token 的生成和解析├── claims.go // 从请求…...

【拥抱AI】Deer-Flow字节跳动开源的多智能体深度研究框架

最近发现一款可以对标甚至可能超越GPT-Researcher的AI深度研究应用,Deer-Flow(Deep Exploration and Efficient Research Flow)作为字节跳动近期开源的重量级项目,正以其模块化、灵活性和人机协同能力引发广泛关注。该项目基于 La…...

第六天——贪心算法——字符串分隔

1. 题目 给定一个字符串 s,我们需要将其划分为尽可能多的部分,使得同一字母最多出现在一个部分中。 例如:字符串 "ababcc" 可以划分为 ["abab", "cc"],但要避免 ["aba", "bcc&quo…...

Python 条件语句详解

条件语句是编程中用于控制程序流程的基本结构,Python 提供了几种条件语句来实现不同的逻辑判断。 1. if 语句 最基本的条件语句形式: if 条件:# 条件为真时执行的代码块示例: age 18 if age > 18:print("你已经成年了")2. …...

前端获取用户的公网 IP 地址

可以使用免费的免费的公共服务网站 一:https://www.ipify.org/ 获取 JSON 格式的 IP 地址 // 旧地址不好使 // https://api.ipify.org/?formatjson // 新地址 https://api64.ipify.org/?formatjson 二:https://ipinfo.io/ https://ipinfo.io/ 三&a…...

在Maven中替换文件内容的插件和方法

在Maven中替换文件内容的插件和方法 Maven提供了几种方式来替换文件内容&#xff0c;以下是常用的插件和方法&#xff1a; 1. maven-replacer-plugin (推荐) 这是专门用于文件内容替换的插件&#xff0c;功能强大且灵活。 基本配置 <plugin><groupId>com.goog…...

符合Python风格的对象(再谈向量类)

再谈向量类 为了说明用于生成对象表示形式的众多方法&#xff0c;我们将使用一个 Vector2d 类&#xff0c;它与第 1 章中的类似。这一节和接下来的几节会不断实 现这个类。我们期望 Vector2d 实例具有的基本行为如示例 9-1 所示。 示例 9-1 Vector2d 实例有多种表示形式 &g…...

云电竞服务器 工作原理

云电竞服务器工作原理详解 一、核心架构原理 虚拟化资源池‌ 通过 ‌KVM/VMware‌ 等虚拟化技术将物理服务器&#xff08;含NVIDIA GPU集群&#xff09;抽象为可动态分配的算力资源池&#xff0c;每个用户独享独立虚拟机实例&#xff0c;实现硬件资源的按需分配与隔离运行。 …...

【数据结构】线性表--队列

【数据结构】线性表--队列 一.什么是队列二.队列的实现1.队列结构定义&#xff1a;2.队列初始化函数&#xff1a;3.队列销毁函数&#xff1a;4.入队列函数&#xff08;尾插&#xff09;&#xff1a;5.出队列函数&#xff08;头删&#xff09;&#xff1a;6.取队头元素&#xff…...

[Vue3]语法变动

Vue3的语法相对比Vue2有不少改变&#xff0c;这篇讲一下基础语法在Vue3里的形式。 创建Vue对象 在脚手架项目中&#xff0c;index.html等资源不再编写代码&#xff0c;只作为一个容器。所有的页面代码都在.vue相关文件中进行编写&#xff0c;由main.js引入各个.vue文件渲染出页…...

Ubuntu服务器开启SNMP服务 监控系统配置指南 -优雅草星云智控简易化操作

Ubuntu服务器开启SNMP服务 & 监控系统配置指南 -优雅草星云智控简易化操作 一、Ubuntu服务器开启SNMP服务 步骤1&#xff1a;安装SNMP服务 sudo apt update sudo apt install snmp snmpd snmp-mibs-downloader -y 步骤2&#xff1a;配置SNMP&#xff08;编辑配置文件&am…...

linux本地部署ollama+deepseek过程

1.Tags ollama/ollama GitHub 选择一个版本下载&#xff0c;我下的是0.5.12 2.tar解压该文件 3.尝试启动ollama ollama serve 4.查看ollama的版本 ollama -v 5.创建一个系统用户 ollama&#xff0c;不允许登录 shell&#xff0c;拥有一个主目录&#xff0c;并且用…...

从零开始实现大语言模型(十五):并行计算与分布式机器学习

1. 前言 并行计算与分布式机器学习是一种使用多机多卡加速大规模深度神经网络训练过程&#xff0c;以减少训练时间的方法。在工业界的训练大语言模型实践中&#xff0c;通常会使用并行计算与分布式机器学习方法来减少训练大语言模型所需的钟表时间。 本文介绍PyTorch中的一种…...

OpenCV进阶操作:指纹验证、识别

文章目录 前言一、指纹验证1、什么是指纹验证2、流程步骤 二、使用步骤&#xff08;案例&#xff09;三、指纹识别&#xff08;案例&#xff09;1、这是我们要识别的指纹库2、这是待识别的指纹图3、代码4、结果 总结 前言 指纹识别作为生物识别领域的核心技术之一&#xff0c;…...

网络安全-等级保护(等保) 2-5 GB/T 25070—2019《信息安全技术 网络安全等级保护安全设计技术要求》-2019-05-10发布【现行】

################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…...

3D生成新突破:阶跃星辰Step1X-3D开源,可控性大幅提升

Step1X-3D 是由 StepFun 联合 LightIllusions 推出的新一代 高精度、高可控性 3D资产生成框架。基于严格的 数据清洗与标准化流程&#xff0c;我们从 500万 3D资产 中筛选出 200万高质量数据&#xff0c;构建了 标准化的几何与纹理属性数据集&#xff0c;为3D生成提供更可靠的训…...

MySQL数据类型之VARCHAR和CHAR使用详解

在设计数据库字段时&#xff0c;字符串类型算是最常见的数据类型之一了&#xff0c;这篇文章带大家深入探讨一下MySQL数据库中VARCHAR和CHAR数据类型的基本特性&#xff0c;以及它们之间的区别。 VARCHAR类型 VARCHAR&#xff08;Variable Character&#xff0c;可变长度字符…...

数字人 LAM 部署笔记

目录 windos踩坑 GitHub - aigc3d/LAM: [SIGGRAPH 2025] LAM: Large Avatar Model for One-shot Animatable Gaussian Head windos踩坑 nvidia-smi环境 cuda 11.8conda install -c "nvidia/label/cuda-11.8.0" cuda-toolkit conda create --name cuda11.8 -y pyth…...

《Docker 入门与进阶:架构剖析、隔离原理及安装实操》

1 docker 简介 1.1 Docker 的优点 Docker 是一款开放平台&#xff0c;用于应用程序的开发、交付与运行&#xff0c;能将应用和基础架构分离&#xff0c;实现软件快速交付 &#xff0c;还能以统一方式管理应用和基础架构&#xff0c;缩短代码从编写到上线的时间。其核心优势如…...

基于Akamai云计算平台的OTT媒体点播转码解决方案

点播视频&#xff08;VOD&#xff09;流媒体服务依赖于视频流的转码来高效分发内容。在转码工作流程中&#xff0c;视频被转换为适合观看设备、网络条件和性能限制的格式。视频转码是计算密集型过程&#xff0c;因此最大化可用硬件上可转码的视频流数量是首要考虑因素。不同基础…...

【MySQL】02.数据库基础

1. 数据库的引入 之前存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件存储存在安全性问题&#xff0c;文件不利于数据查询和管理&#xff0c;文件不利于存储海量数据&#xff0c;文件在程序中控制不方便。而为了解决上述问题&#xff0c;专家们设计出更加利于…...

选错方向太致命,华为HCIE数通和云计算到底怎么选?

现在搞HCIE的兄弟越来越多了&#xff0c;但“数通和云计算&#xff0c;到底考哪个&#xff1f;”这问题&#xff0c;依旧让不少人头疼。 一个是华为认证的老牌王牌专业——HCIE数通&#xff0c;稳、系统、岗位多&#xff1b; 一个是新趋势方向&#xff0c;贴合云原生、数字化…...

经典启发算法【早期/启发式/HC爬山/SA模拟退火/TS禁忌搜/IA免疫 思想流程举例全】

文章目录 一、早期算法二、启发式算法三、爬山法HC3.1 基本思路3.2 伪代码 四、模拟退火SA4.1 算法思想4.2 基本流程4.3 再究原理4.3.1 Metropolis准则4.3.2 再理解 4.4 小Tips4.5 应用举例4.5.1 背包问题&#xff1a;分析&#xff1a;求解&#xff1a; 4.5.2 TSP问题&#xff…...

IntraWeb 16.0.2 + Bootstrap 4 居中布局实战(附源码+效果图)

前言 最近在优化一个 IntraWeb 16.0.2 项目时&#xff0c;发现默认布局方式不够灵活&#xff0c;尤其是在不同屏幕尺寸下对齐效果不佳。于是&#xff0c;我决定引入 Bootstrap 4 来实现 完美居中布局&#xff0c;并成功落地&#xff01;今天就把完整的 源代码 实际效果图 分享…...

Spring 框架中适配器模式的五大典型应用场景

Spring 框架中适配器模式的应用场景 在 Spring 框架中&#xff0c;适配器模式&#xff08;Adapter Pattern&#xff09;被广泛应用于将不同组件的接口转化为统一接口&#xff0c;从而实现组件间的无缝协作。以下是几个典型的应用场景&#xff1a; 1. HandlerAdapter - MVC 请…...

【Java ee初阶】jvm(3)

一、双亲委派机制&#xff08;类加载机制中&#xff0c;最经常考到的问题&#xff09; 类加载的第一个环节中&#xff0c;根据类的全限定类名&#xff08;包名类名&#xff09;找到对应的.class文件的过程。 JVM中进行类加载的操作&#xff0c;需要以来内部的模块“类加载器”…...

C 语言多维数组:定义、初始化与访问的深度解析

各类资料学习下载合集 ​​https://pan.quark.cn/s/8c91ccb5a474​​ 在 C 语言中,我们已经熟悉了一维数组(存储线性数据)和二维数组(存储表格或矩阵数据)。但现实世界的数据结构往往更加复杂,例如表示空间中的点、图像数据、物理模拟的网格等。这时,就需要用到多维数…...

浅入ES5、ES6(ES2015)、ES2023(ES14)版本对比,及使用建议---ES6就够用(个人觉得)

JavaScript&#xff08;ECMAScript&#xff09;的发展经历了多个版本&#xff0c;每个版本都引入了新特性和改进。以下仅是对三个常用版本&#xff08;ES5、ES6&#xff08;ES2015&#xff09; 和 ES2023&#xff09;的基本对比及使用建议&#xff1a; 目前常见项目中还是用ES6…...

23种设计模式考试趋势分析之——适配器(Adapter)设计模式——求三连

文章目录 一、考点分值占比与趋势分析二、真题考点深入挖掘三、"wwwh"简述四、真题演练与解析五、极简备考笔记 适配器模式核心要点六、考点记忆顺口溜七、多角度解答 一、考点分值占比与趋势分析 由于知识库提供的真题年份信息不完整&#xff0c;我们仅能对现有数据…...

Python 翻译词典小程序

一、概述 本工具是基于Python开发的智能翻译系统&#xff0c;采用有道词典进行翻译&#xff0c;并具有本地词典缓存以及单词本功能。 版本号&#xff1a;v1.0 (2025-05-15) 二、核心功能说明 1. 基础翻译功能 即时翻译&#xff1a;输入英文单词自动获取中文释义 词性识别&…...