JWT介绍

JWT

JSON Web Token (JWT) 是一种开放标准 (RFC 7519),提供一种简洁且自包含的方式,以JSON形式在通信双方间传递信息。这些信息可通过数字签名进行验证,确保其可信度。JWT 可以使用密钥(HMAC)或 RSA 或 ECDSA 的公钥/私钥对进行签名。虽然JWT可以加密以提供私密消息,但我们一般指的是已签名的 tokens。

JWT作用

  1. 身份验证和授权:JWT常被用来在身份提供者和服务提供者之间传递被认证的用户身份信息,以便于从资源服务器获取资源。这种方式特别适用于分布式站点的登录场景357。此外,JWT还可以包含用户标识、用户角色和权限等信息,进一步实现细粒度的授权控制。
  2. 信息交换:由于JWT的载荷部分可以自定义,它可以存储任何JSON格式的数据,这使得JWT成为一种便捷的方式来进行信息交换。例如,可以使用JWT来存储用户喜好、配置信息等自定义功能所需的信息。
  3. 单点登录(SSO):JWT因其小巧和自包含的特性,特别适合用于实现单点登录(SSO)。在单点登录场景中,用户只需进行一次登录操作,便可以在多个服务之间自由切换,而无需重复登录,极大地提高了用户体验。
  4. API密钥管理:JWT也广泛应用于API密钥管理领域。通过使用JWT作为API请求的一部分,可以有效地管理和控制对API的访问权限。
  5. 替代传统的Session和Cookie机制:在某些情况下,JWT可以用来替代传统的Session和Cookie机制。与Session和Cookie相比,JWT具有更小的服务器开销和更好的扩展性,因为它直接将用户数据下发给客户端,每次请求时附带发送给服务器。
  6. 跨域请求(Cross-Origin Requests):JWT可用于跨域请求的身份验证。由于JWT是自包含的,它不依赖于任何服务器端的会话存储,因此非常适合用于跨域API调用。

认证流程

  1. 前端通过Web将用户名和密码发送至后端,通常采用HttpPost请求。
  2. 后端在核对用户名和密码后,会将用户ID及其他信息作为JWT payload,并生成一个token返回给前端。
  3. 前端保存这个token,可以选择将其保存在localStorage或sessionStorage中,退出登录时删除token即可。
  4. 在后续的每次请求中,前端需要在header中放入token。
  5. 后端会检查token的有效性,包括签名是否正确,token是否过期等。
  6. 当后端验证token通过后,就会进行业务处理,并返回相应结果。

JWT的组成:

一个JWT实际上由三个部分组成,它们之间用点(.)分隔:

  1. 头部(Header):
    • 通常包含两个部分:token的类型(typ),这里固定为JWT;以及所使用的签名算法(alg),例如HS256表示使用HMAC和SHA256算法进行签名。
    • 例如:{"alg": "HS256", "typ": "JWT"}
  2. 载荷(Payload):
    • 包含了一系列可以被添加到JWT中的声明(claims)。这些声明可以分为三类:
      • 公共声明(Public Claims): 任何JWT都可以包含的声明,如exp(过期时间)、iat(发行时间)和sub(主题)。
      • 注册声明(Registered Claims): 一组预定义的声明,具有特定的语义意义,如iss(发行者)、aud(接收方)等。
      • 私有声明(Private Claims): 由应用程序定义的声明,可以在标准声明之外添加额外的信息。
    • 例如:{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
  3. 签名(Signature):
    • 为了确保JWT的安全性,使用头部中指定的算法和服务器的密钥对头部和载荷进行签名。接收方可以使用相同的密钥和算法来验证签名的有效性。
    • 签名的生成通常使用Base64 URL编码的头部和载荷,然后与密钥一起通过签名算法进行加密。

将这三部分编码(Base64 URL编码)并用点连接起来,就形成了一个完整的JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cU5O62AvvewJHsfqh1EJpTbuE7sBqVb64hjSDgZSTrZDRbky3ofGkeXBXBQgTJgzTyTTh0b2tlbg

在这个例子中,JWT由三部分组成,每部分都用.分隔。第一部分是头部,指定了签名算法;第二部分是载荷,包含了用户信息和过期时间等声明;第三部分是签名,用于验证JWT的真实性。

代码实例

1.安装JWT库

安装golang-jwt/jwt库

go get -u [github.com/golang-jwt/jwt](http://github.com/golang-jwt/jwt)

2.创建JWT密钥

import ("github.com/golang-jwt/jwt"
)// 生成JWT密钥
key := jwt.New(jwt.SigningMethodHS256)

3.创建JWT令牌

创建JWT令牌时,你需要定义载荷(Payload)中的声明(Claims),并使用密钥对令牌进行签名

import ("github.com/golang-jwt/jwt""time"
)
//创建JWT令牌
func createToken(claims map[string]interface{})(string,error){//创建JWT令牌token:=jwt.NewWithClaims(key,jwy.MapClaims(claims))//设置过期时间token.Claims.(jwt.MapClaims)["exp"]=time.Now().Add(5*time.Minute).Unix()//签发令牌tokenString, err := token.SignedString()if err != nil {return "", err}return tokenString, nil
}

4.验证JWT令牌

验证JWT令牌时,你需要使用相同的密钥和签名方法来验证签名的有效性,并检查令牌是否过期:

// parseToken 从给定的token字符串中解析出一个有效的jwt.Token实例。
// 函数接受一个tokenString参数,表示待解析的JWT字符串,
// 并返回一个指向jwt.Token的指针以及一个error。
// 如果解析成功,返回的jwt.Token将包含解码后的JWT声明信息,
// 否则返回一个非nil error说明解析失败的原因。
func parseToken(tokenString string)(*jwt.Token,error){
// 使用jwt.ParseWithClaims方法解析tokenString,// 将声明部分解析为jwt.MapClaims类型(一个可索引的声明映射)。// 提供一个自定义的解析密钥验证函数作为第三个参数。tokne,err:=jwt.ParseWithClaims(tokenString,&jwt.MapClaims{},func(tokekn *jwt.Token)(interface{},error){return key,nil})if err!=nil{return nil,error}// 如果token解析成功且有效(即其声明经过验证且未过期等),// 则尝试将其声明部分转换为jwt.MapClaims类型,并检查转换是否成功。// 若成功且token仍处于有效状态(token.Valid == true),则返回解析得到的token及其原始声明。if claims, ok := token.Claims.(*jwt.MapClaims); ok && token.Valid {return token, nil}// 如果上述条件均不满足(即解析或验证过程中出现错误,// 或token已过期、被篡改等导致无效),则返回一个表示无效JWT的预定义错误。return nil, jwt.ErrInvalid
}

此函数的主要功能是接收一个JWT字符串,通过调用jwt.ParseWithClaims方法对其进行解析。在解析过程中,它会使用传入的密钥(key)验证JWT的签名,确保其完整性和有效性。如果解析和验证成功,函数返回解析后的jwt.Token实例以及nil错误。否则,返回niljwt.Token指针和相应的错误信息。

5.实现登录和注册逻辑

在后端服务中,实现登录和注册逻辑,并在登录成功后返回JWT令牌:

func login(w http.ResponseWriter, r *http.Request) {// 假设你已经验证了用户的凭据// 创建JWT令牌claims := jwt.MapClaims{"userID": "user-id","exp":   time.Now().Add(5 * time.Minute).Unix(),}tokenString, err := createToken(claims)if err != nil {// 处理错误}// 返回JWT令牌w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(map[string]string{"token": tokenString})
}

6. 保护路由

创建一个中间件来保护你的路由,确保只有携带有效JWT令牌的请求才能访问:

// requireAuth 是一个HTTP中间件函数,用于对请求进行JWT身份验证。
// 它接收一个http.HandlerFunc类型的next参数,代表待执行的下一个HTTP处理器。
// 函数返回一个新的http.HandlerFunc,该处理器在实际执行next之前,
// 会对传入的请求进行JWT令牌验证。若验证失败,则返回HTTP 401 Unauthorized响应。
func requireAuth(next http.HandlerFunc) http.HandlerFunc {// 返回一个匿名http.HandlerFunc,作为实际执行的身份验证中间件。return func(w http.ResponseWriter, r *http.Request) {// 从请求头的"Authorization"字段中提取JWT令牌字符串。tokenString := r.Header.Get("Authorization")// 如果请求头中没有提供"Authorization"字段或其值为空,// 则向客户端发送HTTP 401 Unauthorized响应,并附带错误消息。if tokenString == "" {http.Error(w, "Authorization header is required", http.StatusUnauthorized)return}// 使用parseToken函数解析并验证JWT令牌字符串。// 如果解析或验证过程中发生错误,或者令牌本身无效,// 则向客户端发送HTTP 401 Unauthorized响应,并附带错误消息。token, err := parseToken(tokenString)if err != nil || !token.Valid {http.Error(w, "Invalid token", http.StatusUnauthorized)return}// 如果JWT令牌验证成功,继续执行传入的next处理器(即后续的HTTP处理逻辑)。next(w, r)}
}

此代码实现了HTTP中间件requireAuth,其作用是在接收到HTTP请求时,检查请求头中的Authorization字段是否存在并含有有效的JWT令牌。如果请求缺少Authorization字段或令牌无效,中间件会立即向客户端返回HTTP 401 Unauthorized状态码及相应的错误消息。反之,若令牌验证通过,则允许请求继续传递到下一个HTTP处理器(next参数指定的函数)进行后续处理。这样的设计使得您可以轻松地将requireAuth中间件应用于特定路由或全局,以确保只有持有有效JWT令牌的客户端才能访问受保护的资源。
然后,使用这个中间件来保护你的路由:

http.HandleFunc("/protected", requireAuth(func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Welcome to the protected route!")
}))

实例

在Go语言中实现JWT鉴权的完整应用实例。

package mainimport ("fmt""net/http""time"jwtgo "github.com/golang-jwt/jwt"
)// jwtKey 是用于签署 JWT 令牌的秘密密钥,必须保密。
var jwtKey = []byte("my_secret_key")// User 定义了用户的结构体,包含用户的ID和姓名。
// 这些字段将被编码到 JWT 令牌中。
type User struct {ID   int    `json:"id"`   // 用户的唯一标识符Name string `json:"name"` // 用户的姓名
}// createToken 根据提供的 User 对象创建一个新的 JWT 令牌。
// 它设置了一个 "userID" 声明和一个 5 分钟后过期的时间。
func createToken(user *User) (string, error) {// 使用 HS256 签名方法创建一个新的 JWT 实例。token := jwtgo.New(jwtgo.SigningMethodHS256)// 将 token 的 Claims 设置为 MapClaims 类型,以便我们可以添加自定义声明。claims := token.Claims.(jwtgo.MapClaims)// 添加 "userID" 声明到 JWT 载荷中。claims["userID"] = user.ID// 设置 "exp" 声明为当前时间加上 5 分钟的 Unix 时间戳。claims["exp"] = time.Now().Add(5 * time.Minute).Unix()// 使用 jwtKey 签署令牌,并将其转换为字符串。tokenString, err := token.SignedString(jwtKey)if err != nil {return "", err}return tokenString, nil
}// parseToken 接受一个 JWT 令牌字符串,并使用 jwtKey 验证其签名。
// 如果令牌有效,它将返回解析后的 jwtgo.Token 对象。
func parseToken(tokenString string) (*jwtgo.Token, error) {// 使用 jwtgo.ParseWithClaims 解析 JWT 令牌,并使用 MapClaims 类型作为期望的声明类型。token, err := jwtgo.ParseWithClaims(tokenString, &jwtgo.MapClaims{}, func(token *jwtgo.Token) (interface{}, error) {// 提供 jwtKey 作为密钥来验证令牌的签名。return jwtKey, nil})if err != nil {return nil, err}// 确保解析后的声明是 MapClaims 类型。if _, ok := token.Claims.(*jwtgo.MapClaims); !ok {return nil, fmt.Errorf("invalid claims")}// 检查令牌是否有效(例如,是否已过期)。if !token.Valid {return nil, fmt.Errorf("token is not valid")}return token, nil
}// register 是一个 HTTP 处理函数,用于处理用户注册请求。
// 它创建一个新的 User 对象,并为其生成一个 JWT 令牌。
func register(w http.ResponseWriter, r *http.Request) {// 示例中省略了实际的用户注册逻辑(如保存用户信息到数据库)。// 直接创建一个示例用户和令牌。user := &User{ID: 1, Name: "John Doe"}token, err := createToken(user)if err != nil {http.Error(w, "Error creating token", http.StatusInternalServerError)return}// 设置响应头为 application/json 并将令牌编码为 JSON 返回。w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(map[string]string{"token": token})
}// login 是一个 HTTP 处理函数,用于处理用户登录请求。
// 它验证用户凭据(示例中省略),并返回一个 JWT 令牌。
func login(w http.ResponseWriter, r *http.Request) {// 示例中省略了实际的用户登录逻辑(如验证用户凭据)。// 直接创建一个示例用户和令牌。user := &User{ID: 1, Name: "John Doe"}token, err := createToken(user)if err != nil {http.Error(w, "Error creating token", http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(map[string]string{"token": token})
}// protected 是一个 HTTP 处理函数,用于处理需要身份验证的请求。
// 它从 HTTP 头中提取 JWT 令牌,并验证其有效性。
func protected(w http.ResponseWriter, r *http.Request) {// 从 "Authorization" HTTP 头中提取 JWT 令牌。tokenString := r.Header.Get("Authorization")if tokenString == "" {http.Error(w, "Unauthorized access", http.StatusUnauthorized)return}// 解析并验证 JWT 令牌。token, err := parseToken(tokenString)if err != nil {http.Error(w, "Unauthorized access", http.StatusUnauthorized)return}// 如果令牌有效,返回成功响应。w.WriteHeader(http.StatusOK)w.Write([]byte("Welcome to the protected route!"))
}func main() {// 设置 HTTP 路由处理函数。http.HandleFunc("/register", register)http.HandleFunc("/login", login)http.HandleFunc("/protected", protected)// 启动 HTTP 服务并监听 8080 端口。http.ListenAndServe(":8080")
}

这个程序实现了一个简单的 JWT 鉴权系统。它提供了用户注册和登录功能,这两个功能都会生成一个 JWT 令牌。此外,它还提供了一个受保护的路由,该路由只允许携带有效 JWT 令牌的请求访问。

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

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

相关文章

React-hooks:useReducer

useReducer 文档地址 useReducer 是一个 React Hook,它允许你向组件里面添加一个 reducer。 对于拥有许多状态更新逻辑的组件来说,过于分散的事件处理程序可能会令人不知所措。对于这种情况,你可以将组件的所有状态更新逻辑整合到一个外部函…

一文读懂Uniapp的setStorage、setStorageSync、getStorage以及getStorageSync(附Demo)

目录 前言1. uni.setStorage 和 uni.getStorage2. uni.setStorageSync 和 uni.getStorageSync3. 比较 前言 将登陆信息存储在本地,后续将相关信息应用在其他的业务 这类功能需要使用到这几个函数 以及 uni.setStorage 和 uni.getStorage 以及 uni.setStorageSync …

前端 接口返回来的照片太大 加载慢如何解决

现象 解决 1. 添加图片懒加载 背景图懒加载 对背景图懒加载做的解释 和图片懒加载不同&#xff0c;背景图懒加载需要使用 v-lazy:background-image&#xff0c;值设置为背景图片的地址&#xff0c;需要注意的是必须声明容器高度。 <div v-for"img in imageList&quo…

交叉熵损失函数介绍

交叉熵是信息论中的一个重要概念&#xff0c;它的大小表示两个概率分布之间的差异&#xff0c;可以通过最小化交叉熵来得到目标概率分布的近似分布。 为了理解交叉熵&#xff0c;首先要了解下面这几个概念。 自信息 信息论的基本想法是&#xff0c;一个不太可能的事件发生了…

Centos7源码安装Nginx

1. 安装相关依赖 这一步是针对服务器可能是最小化安装的&#xff0c;有些依赖是没有的情况。 服务器可以联网 则执行1.1否则执行1.2 1.1. 可以联网安装依赖 服务器可以联网 则执行这一步安装相关依赖 # 安装nginx需要先将官网下载的源码进行编译&#xff0c;编译依赖gcc环…

openwrt局域网配置多个IP

在局域网配置过程中&#xff0c;若是DHCP服务器关闭&#xff0c;又忘记了配置的ip&#xff0c;将很难访问到路由器重新进行配置。这种情况可以在路由器出厂时做一个备用ip去避免。 1.配置 以下是备用ip的配置方法&#xff0c;以SKYLAB的SKW99 WIFI模组为例进行说明&#xff1…

如何在树莓派安装Nginx并实现固定公网域名访问本地静态站点

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Ngi…

连接完Tomcat服务器后的基本操作

连接完Tomcat服务器后的基本操作 连接完Tomcat后就需要在idea里写接收服务器端数据的代码了。前端的数据由服务器端向后端发送。所以我们要写的是后端接收数据的代码 首先新建一个类继承HttpServlet 无参构造&#xff1a; /*无参构造方法&#xff0c;只被调用一次/一个servl…

LeetCode 59.螺旋矩阵II

LeetCode 59.螺旋矩阵II 1、题目 力扣题目链接&#xff1a;59. 螺旋矩阵 II - 力扣&#xff08;LeetCode&#xff09; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1…

你想来微软苏黎世混合现实研究中心学习和实习吗?

Microsoft Mixed Reality & AI Lab - Zurich 苏黎世混合现实研发中心简介 微软苏黎世混合现实与人工智能实验室概况 专注于利用计算机视觉绘制和理解环境&#xff0c;识别和跟踪相关物体&#xff0c;并帮助用户执行任务&#xff0c;构建混合现实的未来。该实验室还在探索混…

API接口淘宝开放平台item_get-获得淘宝商品详情API接口根据商品ID查询商品标题价格描述等详情数据

要使用淘宝开放平台的item_get API接口根据商品ID查询商品标题、价格、描述等详情数据&#xff0c;你需要先注册一个KEY账号&#xff0c;然后获取到api_key和api_secret。接下来&#xff0c;你可以使用Python的requests库来调用API接口。 以下是一个示例代码&#xff1a; # c…

libftdi1学习笔记 3 - MPSSE GPIO

目录 1. 设置模式 2. gpio状态 3. 初始化gpio状态 4. 写gpio 5. 读gpio 6. 验证 6.1 初始化gpio 6.2 测试代码 MPSSE&#xff0c;即Multi-Protocol Synchronous Serial Engine&#xff0c;是FTDI公司推出的一种多协议同步串行引擎&#xff0c;目前仅FT232H、FT2232H、F…

网络管理实验二、SNMP服务与常用的网管命令

1 常用的网管命令 1.1 网络状态监视命令 包括以下命令&#xff1a;Ipconfig、ping、nslookup、dig、host ipconfig 作用&#xff1a;用来显示本机所有网卡的基本信息&#xff08;IP、掩码、网关、工作状态&#xff09;&#xff1b;用法&#xff1a;ipconfig展示&#xff1a;…

ExtendSim花生酱加工厂模型

该模型展示了ExtendSim可靠性模块与ExtendeSim离散速率技术相结合的协同作用。 在花生酱加工厂的最初阶段&#xff0c;花生经过烘烤和冷却。冷却后的花生经过热烫或水烫去外皮。这些经过漂白的花生进入过程的混合部分&#xff0c;在研磨机中用盐、葡萄糖和氢化油稳定剂将其粉碎…

如何通过Linux pciehp sysfs接口控制PCIe Slot电源状态?-3

pciehp sysfs接口电源控制与NVME驱动卸载的区别 从NVMe SSD设计本身而言&#xff0c;当通过pciehp sysfs接口对PCIe插槽执行Power Off操作时&#xff0c;由于NVMe SSD作为PCIe设备&#xff0c;其电源供应是直接依赖于所连接的PCIe插槽提供的。当插槽电源被关闭时&#xff0c;会…

死锁检测组件

一、死锁 线程 A 占用资源 1,线程 B 占用资源 2,线程 A 想占用资源 2,线程 B 想占用资源 1,线程 A 占用资源 1 的情况下去申请占用资源 2,线程 B 占用资源 2 的情况下去申请占用资源 1,就会出现互相等待对方释放资源的情况,也就是死锁。 CPU 占用率 100% → 出现死循环或…

国产HMI芯片Model3C ——工业品质价格亲民

工业级芯片相较于消费级芯片&#xff0c;在性能上确实拥有显著的优势&#xff0c;尤其对于带彩屏显示或HMI的产品来说&#xff0c;这种优势表现得尤为突出。 首先&#xff0c;对于带彩屏显示或HMI人机交互的产品来说&#xff0c;高性能的芯片是保证流畅的用户体验和快速响应的…

阿里云对象存储OSS批量上传,单个上传,批量删除,单个删除!

请自行替换秘钥&#xff1a; #阿里云 OSS src/main/resources/application.properties #不同的服务器&#xff0c;地址不同 aliyun.oss.file.endpointhttps://oss-cn-hangzhou.aliyuncs.com aliyun.oss.file.accessKeyIdLTAI5t9wUqCoD42qPGRy8S aliyun.oss.file.accessKeySecre…

Spring中的适配器模式

在Spring MVC框架中&#xff0c;适配器模式主要体现在对不同类型的处理器&#xff08;即Controller&#xff09;的统一处理上。Spring MVC通过适配器模式来保证无论Controller的实现方式如何多样化&#xff0c;都能够被DispatcherServlet统一调用和管理。具体使用方式如下&…

如何AI绘画(动漫风格)

绘画描述&#xff1a;一只金毛犬 绘画结果&#xff1a; 如何AI绘画&#xff08;动漫风格&#xff09;&#xff1a; 关注一休哥助手微信公众号 点击小程序按钮打开一休哥助手小程序 切换到我&#xff0c;复制授权码 登录网站&#xff1a;https://www.fudai.fun/drawing&…