GoLong的学习之路(十七)基础工具之Gin框架使用JWT(前后端分离)

文章目录

  • JWT
    • 安装JWT
    • 使用
      • 什么是Claims
      • 默认Claims
      • 自定义Claims
      • 生成JWT
      • 解析JWT
  • 在gin框架中使用JWT
    • 获取Token渠道
    • 定义方法
    • 设置中间件
    • 注册路由
  • 总结一下

JWT

JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下。

诺是不用这个JWT我们就需要去使用Cookie-Session模式实现用户认证。

流程为:

  1. 用户在浏览器端填写用户名和密码,并发送给服务端
  2. 服务端对用户名密码校验通过后会生成一份保存当前用户相关信息的session数据和一个与之对应的标识(通常称为session_id
  3. 服务端返回响应时将上一步的session_id写入用户浏览器的Cookie
  4. 后续用户来自该浏览器的每次请求都会自动携带包含session_idCookie
  5. 服务端通过请求中的session_id就能找到之前保存的该用户那份session数据,从而获取该用户的相关信息。

这种方案依赖于客户端(浏览器)保存 Cookie,并且需要在服务端存储用户的session数据。

在移动互联网时代,我们的用户可能使用浏览器也可能使用APP来访问我们的服务,我们的web应用可能是前后端分开部署在不同的端口,有时候我们还需要支持第三方登录,这下Cookie-Session的模式就有些力不从心了。

JWT就是一种基于Token的轻量级认证模式,服务端认证通过后,会生成一个JSON对象,经过签名后得到一个Token(令牌)再发回给用户,用户后续请求只需要带上这个Token,服务端解密之后就能获取该用户的相关信息了。

安装JWT

我们使用 Go 语言社区中的 jwt 相关库来构建我们的应用,例如:https://github.com/golang-jwt/jwt

方法1:

go get github.com/golang-jwt/jwt/v4

方法2

import "github.com/golang-jwt/jwt/v4"

使用

什么是Claims

JWT tokenpayload 部分是一个json串,是要传递数据的一组声明,这些声明被JWT标准称为claims。 JWT标准里面定义的标准claim包括:

  • iss(Issuser):JWT的签发主体;
  • sub(Subject):JWT的所有者;
  • aud(Audience):JWT的接收对象;
  • exp(Expiration time):JWT的过期时间;
  • nbf(Not Before):JWT的生效开始时间;
  • iat(Issued at):JWT的签发时间;
  • jti(JWT ID):是JWT的唯一标识

默认Claims

如果我们直接使用JWT中默认的字段,没有其他定制化的需求则可以直接使用这个包中的和方法快速生成和解析token。

// 用于签名的字符串
var mySigningKey = []byte("liwenzhou.com")// GenRegisteredClaims 使用默认声明创建jwt
func GenRegisteredClaims() (string, error) {// 创建 Claimsclaims := &jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), // 过期时间Issuer:    "qimi",                                             // 签发人}// 生成token对象token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)// 生成签名字符串return token.SignedString(mySigningKey)
}// ParseRegisteredClaims 解析jwt
func ValidateRegisteredClaims(tokenString string) bool {// 解析tokentoken, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {return mySigningKey, nil})if err != nil { // 解析token失败return false}return token.Valid
}

自定义Claims

除了以上标准声明以外。还有因为自己的需求不同来决定:JWT中来保存那些数据。

例子:我们规定在JWT中要存储username信息,那么我们就定义一个MyClaims结构体。

// CustomClaims 自定义声明类型 并内嵌jwt.RegisteredClaims
// jwt包自带的jwt.RegisteredClaims只包含了官方字段
// 假设我们这里需要额外记录一个username字段,所以要自定义结构体
// 如果想要保存更多信息,都可以添加到这个结构体中
type CustomClaims struct {// 可根据需要自行添加字段Username             string `json:"username"`jwt.RegisteredClaims        // 内嵌标准的声明
}

自定义签名的字符串的过期时间

const TokenExpireDuration = time.Hour * 24

自定义签名

// CustomSecret 用于加盐的字符串
var CustomSecret = []byte("秋天秋天悄悄过去,留下小秘密")

生成JWT

根据自己的业务需要封装一个生成 token 的函数。

// GenToken 生成JWT
func GenToken(username string) (string, error) {// 创建一个我们自己的声明claims := CustomClaims{username, // 自定义字段jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),Issuer:    "my-project", // 签发人},}// 使用指定的签名方法创建签名对象token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)// 使用指定的secret签名并获得完整的编码后的字符串tokenreturn token.SignedString(CustomSecret)
}

解析JWT

jwt.ParseWithClaims()

// ParseToken 解析JWT
func ParseToken(tokenString string) (*CustomClaims, error) {// 解析token// 如果是自定义Claim结构体则需要使用 ParseWithClaims 方法token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, err error) {// 直接使用标准的Claim则可以直接使用Parse方法//token, err := jwt.Parse(tokenString, func(token *jwt.Token) (i interface{}, err error) {return CustomSecret, nil})if err != nil {return nil, err}// 对token对象中的Claim进行类型断言if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { // 校验tokenreturn claims, nil}return nil, errors.New("invalid token")
}

在gin框架中使用JWT

获取Token渠道

我们注册一条路由/auth,对外提供获取Token的渠道

r.POST("/auth", authHandler)

定义方法

authHandler定义

func authHandler(c *gin.Context) {// 用户发送用户名和密码过来var user UserInfoerr := c.ShouldBind(&user)if err != nil {c.JSON(http.StatusOK, gin.H{"code": 2001,"msg":  "无效的参数",})return}// 校验用户名和密码是否正确if user.Username == "q1mi" && user.Password == "q1mi123" {// 生成TokentokenString, _ := GenToken(user.Username)c.JSON(http.StatusOK, gin.H{"code": 2000,"msg":  "success","data": gin.H{"token": tokenString},})return}c.JSON(http.StatusOK, gin.H{"code": 2002,"msg":  "鉴权失败",})return
}

设置中间件

// 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 == "" {c.JSON(http.StatusOK, gin.H{"code": 2003,"msg":  "请求头中auth为空",})c.Abort()return}// 按空格分割parts := strings.SplitN(authHeader, " ", 2)if !(len(parts) == 2 && parts[0] == "Bearer") {c.JSON(http.StatusOK, gin.H{"code": 2004,"msg":  "请求头中auth格式有误",})c.Abort()return}// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它mc, err := ParseToken(parts[1])if err != nil {c.JSON(http.StatusOK, gin.H{"code": 2005,"msg":  "无效的Token",})c.Abort()return}// 将当前请求的username信息保存到请求的上下文c上c.Set("username", mc.Username)c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息}
}

注册路由

注册一个/home路由

r.GET("/home", JWTAuthMiddleware(), homeHandler)func homeHandler(c *gin.Context) {username := c.MustGet("username").(string)c.JSON(http.StatusOK, gin.H{"code": 2000,"msg":  "success","data": gin.H{"username": username},})
}

以上是自己定义的组件,但是咱们可以使用,使用Github上别人封装好的包,比如https://github.com/appleboy/gin-jwt。用法是直接调用API就成。

总结一下

这种方式是最为基本的,在go语言中Token。应用非常广泛。而以上的token是最为基础的Token,在官方文案里有说明这种Token名字叫access Token

我在这里简单说明一下:

访问令牌(acces Token)是用于访问受保护资源的凭据。在上面的文章中有写道。传统的访问运用的Cookie-Session的模式,但是想必大家,肯定有所耳闻,这种模式下的cookie值并不安全。在抓包工具下。会直接暴漏。

为了增加安全性,此时就需要对信息进行加密。

访问令牌(access Token)是一个是一个字符串,表示一个授权的客户端。这个字符串对于客户端来说是看不清的,无法直接识别。

令牌中间件是由资源服务器和授权强制执行服务器,授权持续时间和影响范围给所有者。

令牌的主要作用就是就是作为检索的标识符,或者,携带某种验证方式(一些数据和一个签名)(类似于校验和)。

在Token中,有一个抽象层,这个抽象层,会赋予资源服务器权限(单一实现)。这个抽象层会提供一个各种方法的验证身份的方法。

访问令牌可以具有不同的格式、结构和方法基于资源的利用率(例如,加密属性)服务器安全要求。访问令牌属性和访问受保护资源的方法不在本文的范围之内此规范和由配套规范定义。

我简单而言就是说对于token在保护信息的作用上,又可以实现类似JavaspringAOP 的拦截验证的机制。

我后续会给一个详细的Token的介绍。

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

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

相关文章

【LeetCode】415 字符串相加

415. 字符串相加 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。 你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。 示例 1&#xff1a…

UNVEILING A CORE LINGUISTIC REGION IN LARGE LANGUAGE MODELS

本文是LLM系列文章,针对《UNVEILING A CORE LINGUISTIC REGION IN LARGE LANGUAGE MODELS》的翻译。 揭示大型语言模型中的核心语言区域 摘要1 引言2 前言和背景3 核心语言能力区4 讨论和未来工作5 结论 摘要 大脑定位描述了大脑特定区域与其相应功能之间的联系&a…

Window下SRS服务器的搭建

---2023.7.23 准备材料 srs下载:GitHub - ossrs/srs at 3.0release 目前srs release到5.0版本。 srs官方文档:Introduction | SRS (ossrs.net) Docker下载:Download Docker Desktop | Docker 进入docker官网选择window版本直接下载。由…

系列四十七、Spring的事务传播行为案例演示(七)#NOT_SUPPORTED

一、演示Spring的传播行为(NOT_SUPPORTED) 1.1、StockServiceImplNOT_SUPPORTED /*** Author : 一叶浮萍归大海* Date: 2023/10/30 15:43* Description: 演示NOT_SUPPORTED的传播行为* 外部不存在事务:不开启新的事务* 外部存在…

1.使用tensorflow

1.张量和操作 tensorflow对张量的操作实际上和numpy差不多,不够有所差距,numpy的数据可以随时被修改,但是tensorflow的数据要分情况。 (1).使用tf.Constant() a tf.Constant([[1,2,3],[4,5,6]]) 这个矩阵就是2*3的矩阵,但是它无…

7.多线程之单例模式

单例模式 文章目录 单例模式1. 什么是单例模式2. 饿汉模式3. 懒汉模式3.1 单线程版:3.2 多线程版 1. 什么是单例模式 单例模式是一种设计模式,常见的设计模式还有工厂模式、建造者模式等。 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码…

Vue3前端100个必要的知识点

为什么是必要的,就是这100个知识点学完后,能独立完成一个小项目。最终能得到一个解决方案。也算是前端知识的积累。如果后面有需要的地方可以回来查。100个其实比较多,我会按新手老鸟,大神来分成3个等级,话不多说&…

SQLyog连接数据库报plugin caching_sha2_password could not be loaded......解决方案

问题描述 问题分析 因为MySQL新版默认使用caching_sha2_password作为身份验证的插件,而旧版本使用的是mysql_native_password。当出现plugin caching_sha2_password could not be loaded报错,我们更换为旧版本 如何解决 先使用cmd命令登录MySQL&a…

【IDEA】设置sql提示

第一步:注入SQL语言 1.首先选择任意一条sql语句,右击,选择 ‘显示上下文操作’ 2.选择 ‘注入语言或引用’ 3. 往下翻,找到MySQL 第二步:配置MySQL数据库连接 1.首先点击侧边的数据库,再点击上面的加号 2…

蓝桥杯刷题

欢迎来到Cefler的博客😁 🕌博客主页:那个传说中的man的主页 🏠个人专栏:题目解析 🌎推荐文章:题目大解析(3) 👉🏻最大降雨量 原题链接&#xff1…

UIKit-WKNavigationDelegate

WKNavigationDelegate 是webKit框架中的一个代理协议,用于处理webVIew导航和与导航相关的事件 WKWebView 是IOS 8引入的一个高性能web视图控件, 相关API webView:didCommitNavigation 导航已经陈工完成且页面内容已经加载时调用此方法,这…

前端移动高级web详细解析五

响应式布局方案 媒体查询 Bootstrap框架 01-媒体查询 基本写法 max-width:最大宽度(小于等于) min-width:最小宽度(大于等于) 书写顺序 min-width(从小到大) max-width&…

MySQL数据库入门到精通——运维篇(1)

MySQL数据库入门到精通——运维篇(1) 1. 日志1.1 错误日志1.2 二进制日志1.3 查询日志1.4 慢查询日志 2. 主从复制2.1 主从复制的概述2.2 主从复制的原理2.3 主从复制的搭建2.3.1 服务器准备2.3.2 主库配置2.3.3 从库配置2.3.4 测试 1. 日志 在任何一种…

边界缩小维护最值——倒序枚举/中部切开:1101T2

http://cplusoj.com/d/senior/p/CPNOIPB 发现维护边界缩小类最值很难做,有两种常见方法: 倒序进行,边界就变成扩大了在 m i d mid mid 处切开,复杂度可以均摊

python实现MC协议(SLMP 3E帧)的TCP服务端(篇一)

python实现MC协议(SLMP 3E帧)的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样,可以使用现成的pymodbus模块去实现。但是,我们可以根据协议帧进行组包,自己去实现帧的格式,而这一切可以基于socket模…

记录 vue + vuetify + electron 安装过程

NodeJs 版本: 20 内容来自: Electron Vue.js Vuetify 构建跨平台应用_思月行云的博客-CSDN博客文章浏览阅读61次。Go coding!https://blog.csdn.net/kenkao/article/details/132600542 npm config set registry https://registry.npm.taobao.org np…

【c++|opencv】二、灰度变换和空间滤波---2.直方图和均衡化

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 图像直方图、直方图均衡化 1. 图像直方图 #include <iostream> #include <opencv2/opencv.hpp>using namespace cv; using namespace std;…

Android NDK开发详解之调试和性能分析的系统跟踪概览

Android NDK开发详解之调试和性能分析的系统跟踪概览 系统跟踪指南 “系统跟踪”就是记录短时间内的设备活动。系统跟踪会生成跟踪文件&#xff0c;该文件可用于生成系统报告。此报告有助于您了解如何最有效地提升应用或游戏的性能。 有关进行跟踪和性能分析的全面介绍&#x…

2010年NOIP普及组第二轮第1题题解(原创)

1&#xff0e;数字统计 (two.pas/c/ cpp) 【问题描述】 请统计某个给定范围[L,R]的所有整数中&#xff0c;数字2出现的次数。 比如给定范围[2,22]&#xff0c;数字2在数2中出现了1次&#xff0c;在数12中出现1次&#xff0c;在数20中出现1次&#xff0c;在数21中出现1次&#x…

groovy下载与安装

Groovy是一种基于JVM&#xff08;Java虚拟机&#xff09;的敏捷开发语言&#xff0c;它结合了Python、Ruby和Smalltalk的许多强大的特性&#xff0c;Groovy 代码能够与 Java 代码很好地结合&#xff0c;也能用于扩展现有代码。由于其运行在 JVM 上的特性&#xff0c;Groovy也可…