- 在VisualStudio中部署GDAL库的C++版本(包括SQLite、PROJ等依赖)
- Android开机流程介绍
- STM32CubeMX教程31USB_DEVICE-HID外设_模拟键盘或鼠标
- 深入浅出Java多线程(五):线程间通信
Json web token
(JWT
), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519
)服务器需要存储用户的token信息 。
服务端不需要存储用户token, 都存在客户端 。
JWT就是一段字符串, 由三段信息构成, 三段信息文本使用.(点) 拼接就构成了JWT字符串
eyJhbGciOiJIUzI1sNiIsIn.eyJzdWIiOiIxMjRG9OnRydWV9.TJVArHDcEfxjoYZgeFONFh7HgQ
header
payload
(类似于飞机上承载的物品)signature
头部,JWT 的元数据,也就是描述这个 token 本身的数据,一个 JSON 对象。由两部分组成
// 定义头部信息
header := map[string]interface{}{
"alg": "HS256", // 声明加密算法,可以根据需要修改
"typ": "JWT", // 声明类型
}
将头部使用base64编码构成第一部分 (base64编码方法, 该编码可以对称解码) 。
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
)
func main() {
// 定义头部信息
header := map[string]interface{}{
"alg": "HS256", // 声明加密算法,可以根据需要修改
"typ": "JWT", // 声明类型
}
// 将头部信息序列化为JSON格式字符串
headerBytes, err := json.Marshal(header)
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
headerStr := base64.RawURLEncoding.EncodeToString(headerBytes)
fmt.Println(headerStr)
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
}
存放用户有效信息的地方,一个 JSON 对象, 这些有效信息包含三个部分:
iss
: JWT签发者sub
: JWT所面向的用户aud
: 接收JWT的一方exp
: JWT的过期时间,这个过期时间必须要大于签发时间nbf
: 定义在什么时间之前,该JWT都是不可用的iat
: JWT的签发时间jti
: JWT的唯一身份标识,主要用来作为一次性token,从而回避时序攻击公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息。但不建议添加敏感信息,因为该部分在客户端可解密.
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息.
除了上面的字段, 你自己也可以添加自己想要的字段, 需要注意的是:这些信息是不加密的, 所以最好不要存敏感信息 。
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
)
func main() {
// 定义Payload信息
payload := map[string]interface{}{
"sub": "1234567890", // 主题,表示该JWT的所有者
"name": "John Doe", // 自定义声明,可以根据需要添加其他声明
"iat": 1516239022, // 签发时间,表示JWT的签发时间,一般为当前时间的时间戳
"exp": 1516239022 + 3600, // 过期时间,表示JWT的过期时间,一般为签发时间加上有效期,以秒为单位
"roles": []string{"admin", "user"}, // 自定义声明,可以存储用户角色等信息
}
// 将Payload信息序列化为JSON格式字符串
payloadBytes, err := json.Marshal(payload)
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
payloadStr := base64.RawURLEncoding.EncodeToString(payloadBytes)
fmt.Println(payloadStr) // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
}
然后将其进行base64加密,得到JWT的第二部分.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
signature 是根据 header 和 token 生成, 由三部分构成
这个部分需要将base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了JWT的第三部分.
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
)
func main() {
// 定义头部信息
header := map[string]interface{}{
"alg": "HS256",
"typ": "JWT",
}
// 定义Payload信息
payload := map[string]interface{}{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516239022 + 3600,
"roles": []string{"admin", "user"},
}
// 将头部信息序列化为JSON格式字符串
headerBytes, err := json.Marshal(header)
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
headerStr := base64.RawURLEncoding.EncodeToString(headerBytes)
// 将Payload信息序列化为JSON格式字符串
payloadBytes, err := json.Marshal(payload)
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
payloadStr := base64.RawURLEncoding.EncodeToString(payloadBytes)
// 定义秘钥
secret := "your-secret-key" // 替换为实际的秘钥
// 生成签名
signature := generateSignature(headerStr, payloadStr, secret)
fmt.Println(signature) // C-94Wc6olGK6CEbkA9Xj0ogDQIFdPsEefZKCZrz_fvA
// 生成的签名字符串
}
func generateSignature(headerStr, payloadStr, secret string) string {
// 构造要签名的数据
dataToSign := headerStr + "." + payloadStr
// 使用HMAC-SHA256算法生成签名
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(dataToSign))
signatureBytes := h.Sum(nil)
// 对签名进行base64编码
signature := base64.RawURLEncoding.EncodeToString(signatureBytes)
return signature
}
算出签名之后, 把 header、payload、signatrue 三部分使用 .(点) 拼接成一个大字符串, 然后返回给客户端让其存储 。
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
)
func main() {
// 定义头部信息
header := map[string]interface{}{
"alg": "HS256",
"typ": "JWT",
}
// 定义Payload信息
payload := map[string]interface{}{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516239022 + 3600,
"roles": []string{"admin", "user"},
}
// 将头部信息序列化为JSON格式字符串
headerBytes, err := json.Marshal(header)
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
headerStr := base64.RawURLEncoding.EncodeToString(headerBytes)
// 将Payload信息序列化为JSON格式字符串
payloadBytes, err := json.Marshal(payload)
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
payloadStr := base64.RawURLEncoding.EncodeToString(payloadBytes)
// 将base64加密后的header和payload拼接起来
dataToSign := headerStr + "." + payloadStr
// 定义秘钥
secret := "your-secret-key" // 替换为实际的秘钥
// 生成签名
signature := generateSignature(dataToSign, secret)
// 最终的JWT字符串
jwtToken := dataToSign + "." + signature
fmt.Println(jwtToken)
// 最终生成的JWT字符串
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTYyNDI2MjIsImlhdCI6MTUxNjIzOTAyMiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZXMiOlsiYWRtaW4iLCJ1c2VyIl0sInN1YiI6IjEyMzQ1Njc4OTAifQ.C-94Wc6olGK6CEbkA9Xj0ogDQIFdPsEefZKCZrz_fvA
}
func generateSignature(dataToSign, secret string) string {
// 使用HMAC-SHA256算法生成签名
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(dataToSign))
signatureBytes := h.Sum(nil)
// 对签名进行base64编码
signature := base64.RawURLEncoding.EncodeToString(signatureBytes)
return signature
}
注意:secret 是保存在服务器端的,JWT的签发生成也是在服务器端的,secret 就是用来进行JWT的签发和JWT的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个 secret,那就意味着客户端是可以自我签发JWT了.
首先 base64 是一种编码方式, 并非加密方式; 它跟语言无关, 任何语言都能使用 base64 编码&解码 。
// 定义一个信息字段
dic := map[string]interface{}{"id": 1, "name": "jarvis", "age": "male"}
// 将其序列化成json格式字符串
jsonBytes, err := json.Marshal(dic)
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
jsonStr := string(jsonBytes)
// 将json格式字符串encode再使用base64编码成一串Bytes格式编码
base64Str := base64.StdEncoding.EncodeToString([]byte(jsonStr))
fmt.Println([]byte(base64Str))
// [101 121 74 112 90 67 73 54 73 68 69 115 73 67 50 70 109 90 121 66 67 74 112 73 106 111 103 73 109 70 48 105 71 108 112 77 97 86 120 73 106 111 103 73 109 116 65 87 120 108 73 106 111 103 73 109 116 65 87 120 108 73 106 111 103 73 109 116 65 87 120 108 73 106 111 103 73 61]
fmt.Println(base64Str)
// eyJhZ2UiOiJtYWxlIiwiaWQiOjEsIm5hbWUiOiJqYXJ2aXMifQ==
// 替换为你的 base64 编码字符串
base64Str := "eyJhZ2UiOiJtYWxlIiwiaWQiOjEsIm5hbWUiOiJqYXJ2aXMifQ=="
// base64 解码
decodedBytes, err := base64.StdEncoding.DecodeString(base64Str)
if err != nil {
fmt.Println("Base64 decoding error:", err)
return
}
// JSON 反序列化
var dic map[string]interface{}
err = json.Unmarshal(decodedBytes, &dic)
if err != nil {
fmt.Println("JSON decoding error:", err)
return
}
fmt.Println(dic)
// map[age:male id:1 name:jarvis]
/*
1)jwt分三段式:头.体.签名 (head.payload.sgin)
2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的
3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
"company": "公司信息",
...
}
5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
"user_id": 1,
...
}
6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{
"head": "头的加密字符串",
"payload": "体的加密字符串",
"secret_key": "安全码"
}
*/
根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token 。
token
字符串返回给前台根据客户端带 token 的请求 反解出 user 对象 。
.
(点) 拆分为三段字符串, 第一段编码后的头字符串一般不需要做任何处理用账号密码访问登录接口,登录接口逻辑中调用签发token算法,得到token,返回给客户端,客户端自己存到cookies中.
校验token的算法应该写在中间件中,所有请求都会进行认证校验,所以请求带了token,就会反解出用户信息.
使用Gin框架时,你可以选择一个适用于Go语言的JWT库。一个流行的选择是github.com/dgrijalva/jwt-go库.
go get -u github.com/golang-jwt/jwt/v5
在你的Go代码中导入github.com/golang-jwt/jwt/v5和github.com/gin-gonic/gin.
import (
"github.com/golang-jwt/jwt/v5"
"github.com/gin-gonic/gin"
)
JWT中间件: 创建一个JWT中间件,它将用于保护需要身份验证的路由.
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"net/http"
"strings"
"webook/internal/web"
)
// LoginJWTMiddlewareBuilder JWT 登录校验
type LoginJWTMiddlewareBuilder struct {
paths []string
}
func NewLoginJWTMiddlewareBuilder() *LoginJWTMiddlewareBuilder {
return &LoginJWTMiddlewareBuilder{}
}
// IgnorePaths 忽略的路径
func (l *LoginJWTMiddlewareBuilder) IgnorePaths(path string) *LoginJWTMiddlewareBuilder {
l.paths = append(l.paths, path)
return l
}
func (l *LoginJWTMiddlewareBuilder) Build() gin.HandlerFunc {
// 用 Go 的方式编码解码
return func(ctx *gin.Context) {
// 不需要登录校验的
for _, path := range l.paths {
if ctx.Request.URL.Path == path {
return
}
}
// 用 JWT 来校验
tokenHeader := ctx.GetHeader("Authorization")
if tokenHeader == "" {
// 没登录
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
segs := strings.Split(tokenHeader, " ")
if len(segs) != 2 {
// 没登录,有人瞎搞
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
tokenStr := segs[1]
claims := &web.UserClaims{}
// ParseWithClaims 里面,一定要传入指针
// 这里的95osj3fUD7fo0mlYdDbncXz4VD2igvf0 代表的是签发的时候的key,并且key 要和签发的时候一样
token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
return []byte("95osj3fUD7fo0mlYdDbncXz4VD2igvf0"), nil
})
if err != nil {
// 没登录
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
// token 验证不通过
if token == nil || !token.Valid {
// 没登录
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
// 将用户信息存储到上下文中
ctx.Set("claims", claims)
}
}
使用JWT中间件: 在需要身份验证的路由上使用JWT中间件.
func initWebServer() *gin.Engine {
ser := gin.Default()
ser.Use(cors.New(cors.Config{
//AllowOrigins: []string{"*"},
//AllowMethods: []string{"POST", "GET"},
AllowHeaders: []string{"Content-Type", "Authorization"},
// 允许跨域访问的响应头,不加这个前端拿不到token响应头
ExposeHeaders: []string{"x-jwt-token"},
// 是否允许你带 cookie 之类的东西
AllowCredentials: true,
AllowOriginFunc: func(origin string) bool {
if strings.HasPrefix(origin, "http://localhost") {
// 你的开发环境
return true
}
return strings.Contains(origin, "http://你的公司域名.com")
},
MaxAge: 12 * time.Hour,
}))
// 注册登录校验中间件以及不要登录校验的路径
ser.Use(middleware.NewLoginJWTMiddlewareBuilder().
IgnorePaths("/users/signup").
IgnorePaths("/users/login").Build())
return ser
}
生成JWT token: 在用户登录成功后,你可以生成JWT并将其返回给客户端.
// UserClaims 自定义的声明结构体并内嵌 jwt.StandardClaims
type UserClaims struct {
jwt.RegisteredClaims
// 声明你自己的要放进去 token 里面的数据
Uid int64
// 后续需要什么字段,就在这里添加
}
func (u *UserHandler) LoginJWT(ctx *gin.Context) {
type LoginReq struct {
Email string `json:"email"`
Password string `json:"password"`
}
var req LoginReq
if err := ctx.Bind(&req); err != nil {
return
}
user, err := u.svc.Login(ctx, req.Email, req.Password)
if err == service.ErrInvalidUserOrPassword {
ctx.String(http.StatusOK, "用户名或密码不对")
return
}
if err != nil {
ctx.String(http.StatusOK, "系统错误")
return
}
// 步骤2
// 在这里用 JWT 设置登录态
// 生成一个 JWT token
// 将用户信息存储到token中
claims := UserClaims{
Uid: user.Id,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
tokenStr, err := token.SignedString([]byte("95osj3fUD7fo0mlYdDbncXz4VD2igvf0"))
if err != nil {
ctx.String(http.StatusInternalServerError, "系统错误")
return
}
ctx.Header("x-jwt-token", tokenStr)
fmt.Println(user)
ctx.String(http.StatusOK, "登录成功")
return
}
我们通过接口调试工具访问路由127.0.0.1:8080/users/login 签发用户token,header 中就会有X-Jwt-Token这个字段以及生成的token 对应值.
在平时开发中,我们一般不会直接传user_id 过来,一般是通过token来获取用户信息,比如我们需要查询用户信息,之前我们已经将用户ID放入到token中了,直接通过c, _ := ctx.Get("claims")来获取我们存放的用户信息,以下是具体代码; 。
func (u *UserHandler) ProfileJWT(ctx *gin.Context) {
c, _ := ctx.Get("claims")
// 你可以断定,必然有 claims
//if !ok {
// // 你可以考虑监控住这里
// ctx.String(http.StatusOK, "系统错误")
// return
//}
// ok 代表是不是 *UserClaims
claims, ok := c.(*UserClaims)
if !ok {
// 你可以考虑监控住这里
ctx.String(http.StatusOK, "系统错误")
return
}
fmt.Println("当前用户ID为:", claims.Uid)
ctx.String(http.StatusOK, "查询成功")
}
最后我们只需要访问路由:127.0.0.1:8080/users/profile,在header中加入token 即可.
最后此篇关于Gin框架之jwt介绍与基本使用的文章就讲到这里了,如果你想了解更多关于Gin框架之jwt介绍与基本使用的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
如何将十进制数字转换为mixed radix表示法? 我猜想给定每个基数数组的输入和十进制数,它应该输出每列值的数组。 最佳答案 伪代码: bases = [24, 60, 60] input = 8
我有 Table-A,其中有“x”行。 (对于这个例子有 8 行) 我通过使用游标创建了列数为“x”的Table-C。 (使其动态化;如果将更多行添加到 Table-A,则会在 Table-C 中创建
我有一个关于对象的(很可能是简单而愚蠢的)问题。我创建了实例“Person”的对象“jon”。当我打电话时 console.log(jon.name) 控制台会给我输出“jon”。到目前为止,一切都很
这个问题已经有答案了: 已关闭11 年前。 Possible Duplicate: javascript function vs. ( function() { … } ()); 抱歉,如果这太基础了
我正在尝试用 Java 重新创建射弹轨迹,但是,我遇到了一些问题。我看过很多解释公式之类的视频,但他们的方程中有一个目标,而我没有。我的意思是,他们有一个范围来计算子弹的下落,但我试图弄清楚子弹最终会
(希望如此)来自一个完整的 Rust 初学者的一个简单问题。我的循环有什么问题? num 计算结果为“69”的速度相当快,但是一旦 num 设置为“69”,循环就永远不会退出。我肯定遗漏了一些明显的东
我在 id="name"的元素上应用“.length”,但它计数为 29 而不是 14。我想知道我的错误在哪里?如果有人可以让我知道,那就太好了。谢谢! var name=document.getEl
我知道这很简单,但由于某种原因我无法让它工作。我正在尝试在 Java 中创建自定义颜色,但它似乎不起作用。 import java.awt.Color; Color deepGreen = new C
我有一个大文件,其中每一行都包含一个子字符串,例如 ABC123。如果我执行 grep ABC file.txt 或 grep ABC1 file.txt 我按预期返回这些行,但如果我执行 grep
我想将以下实体映射转换为 Priority 对象。在 getter 上,当我将“Short”更改为“Priority”并遵循 this.priority 时,它会提示 'basic' 属性类型不应该是
我正在开发一个相当基本的函数,我发现很难弄清楚为什么我会得到我的输出。 def mystery(n): print(n) if n < 4: my
我正在尝试对 WordPress 安装的新闻部分实现同位素过滤。我是 JavaScript/jQuery 的新手,正在尝试随时随地学习。我首先使用 Filters section of the Iso
已关闭。这个问题是 not reproducible or was caused by typos 。目前不接受答案。 这个问题是由拼写错误或无法再重现的问题引起的。虽然类似的问题可能是 on-top
我在另一个实体类中引用一个实体并收到此错误。下面是示例代码。我在 persistence.xml 中也有这些类。 是什么导致了这个问题?我正在使用 Spring 数据 JPA 和 Hibernate。
我正在解析 HTML 并重新格式化图像以使其更好地适应。由于某种原因,当我有多个图像需要解析时,我会超出范围,而且我一生都无法弄清楚为什么。 当 imgArray.count >1 时,我将使用带有递
我是 SQL 新手,正在尝试创建一个基本的子查询。我需要找出经理的平均年龄和实习生的平均年龄之间的差异。 标题为一栏 - 经理或实习生年龄是一列,全部在同一个表中。 我会使用两个子查询来做类似的事情:
我习惯了 csh,所以不得不使用 bash 有点烦人。这段代码有什么问题? if[$time > 0300] && [$time 和 300 && time < 900 )) then mod
我建立了这个页面:http://excelwrestling.com/poola.php即将到来的双重锦标赛。我的大部分数据都是从我的 mySQL 数据库中提取的,现在只有一些示例数据。 我希望链接选
是否有任何原因导致以下内容不起作用: for (i=0;i < someArray.length;i++) { if (someArray[i].indexOf("something") !=
我现在正在学习 Javascript,有一个问题一直困扰着我! 因此,我在这里所需要做的就是在此输入框中键入颜色,单击按钮并将标题更改为键入的颜色(仅当键入的颜色位于变量中指定的数组中时)。 我的代码
我是一名优秀的程序员,十分优秀!