JWT基础详解

简介: JWT(JSON Web Token)是一种开放标准(RFC 7519),用于安全传递声明。它通过Header.Payload.Signature三部分构成,支持签名/加密,实现无状态跨域认证,减轻服务器存储压力,广泛应用于现代Web和微服务鉴权场景。

 JSON Web Token 简称JWT

一、起源:

这一切的起源都源于网景公司的一个天才程序员,为了解决http协议无状态问题,就让浏览器承担了一部分“记忆”责任(每次客户端,访问服务器,自身就携带cookie,用于表明自身身份)

由于直接在cookie中携带敏感信息,有用户篡改信息、恶意盗取的安全风险。于是有些公司决定将信息统一管理,只是给用户一个”session_id”,标识自己的身份。下次客户端通过cookie携带这个标识去访问服务器时,服务器能通过该标识作为索引查到用户存在服务器中的信息。

由于这种统一式管理,会压榨服务器的性能。为了给服务器减负,token就应运而生。将相关数据加密签名之后,继续发回客户端。也摆脱了用户篡改信息的可能。

但由于加密算法没有统一,这时jwt就作为一种标准(统一了token格式与验证规则),出来统一了token的江湖,他的作用就像restful规范了API设计一样,让不同系统都能更加顺畅的理解和使用token。

--由多篇cookie、token、restful、jwt博客、视频总结而来

二、定义:

1、跨域认证问题

在互联网早期,传递认证信息的方式大都如下:

1、用户向服务器发送用户名和密码。

2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户信息。

3、服务器向用户返回一个 session_id,写入用户的 Cookie。

4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。

5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

问题: 用户少或者是单机,还能应付过来,若是用户多了,需要多安装几台服务器。就需要持久化层(常见的就是数据库)优点是结构清晰,缺点是工程量大,并且持久层一挂,访问全都变成500了。

最最最重要的是,如果你想要去访问一家公司下的其他网站,由于cookie默认不能跨域,

所以就产生了另一种新思路:

2、JWT原理

另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。

image.gif 编辑

用户与服务器通讯时,都需要发回这个json对象。

为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(后方会详细解释)

此时服务器就不再改变session数据了。(服务器变为了无状态)

3、JWT数据结构

实际上,交互时传递的JWT,长的是这个样子:

image.gif 编辑

它是一个很长的字符串,中间用点(.)分隔成三个部分。(注意jwt是不换行的,这里是方便演示

JWT的3部分如下:

image.gif 编辑

官网介绍:https://jwt.io/introduction 

咱们接下来,详细介绍一下jwt的3个部分

Header

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

image.gif 编辑

alg(algorithm):算法(HS256

typ(type):这个令牌的类型(JWT

最后将上面的JSON对象使用Base64URL算法加密转化成字符串

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

image.gif 编辑

当然你也可以自定义一些:

image.gif 编辑

Signature

这部分通常放置的是一个密钥,这个密钥只有服务器才知道,不能泄露给用户。

然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

image.gif 编辑

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

image.gif 编辑

最后JWT是用如下方式生成:

JWT = base64UrlEncode(header) + "."+ base64UrlEncode(payload) + "."+ 
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 
// 分别对header与payload进行base64url转义编码
// 最后用secret为密钥,通过256算法加密签名

image.gif

4、JWT使用方法

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

通过如下方式存储:

image.gif 编辑

前端可以这样调用:

image.gif 编辑

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

5、JWT特点

1、JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。 (不加密的情况下,不能写私密信息)

2、JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

3、不可变性(不同角度--优缺点),JWT一但签发,不到期之前就不能修改信息(因为服务器不保存session),除非提前安置好了其他手段

4、JWT包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为减少盗用,JWT的有效期应该设置的短些。

5、为了减少盗用,jwt不应该使用HTTP协议明码传输,要使用HTTPS协议传输。

三、应用:

不同的语言,有不同的解决方法。

我这里拿go语言举例说明:

1、创建认证结构体与jwt声明结构体

var jwtKey = []byte("my_secret_key") // 自己生成的密钥
// Credentials 用户凭证结构体
type Credentials struct {
  Username string `json:"username"`
  Password string `json:"password"`
}
// Claims JWT声明结构体
type Claims struct {
  Username             string `json:"username"`
  jwt.RegisteredClaims        // 注册表
}

image.gif

2、创建登入函数,并创建jwt令牌

// loginHandler 处理登入函数
func loginHandler(w http.ResponseWriter, r *http.Request) {
  // 这里简化了验证
  creds := Credentials{
    Username: "123", //r.FormValue("username"),
    Password: "123", //r.FormValue("password"),
  }
  // 实例验证
  // 创建jwt声明
  // 是的,创建jwt
  expirationTime := time.Now().Add(5 * time.Minute)
  claims := &Claims{ // 集合s
    Username: creds.Username,
    RegisteredClaims: jwt.RegisteredClaims{
      ExpiresAt: jwt.NewNumericDate(expirationTime),
    },
  }
  // 生成JWT令牌
  token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
  tokenString, err := token.SignedString(jwtKey)
  if err != nil {
    // 过程
  }
  // 可以通过cookie携带
  http.SetCookie(w, &http.Cookie{
    Name:  "jwt_token",
    Value: tokenString,
    // 可选:加 HttpOnly、Secure 等
    HttpOnly: true,
    Secure:   true, // 生产环境建议开
  })
  // 也可以直接返回Token给客户端 (实际应用应为HttpOnly Cookie)
  w.Write([]byte(tokenString))
}

image.gif

3、中间件验证,并获取令牌

// JWT认证中间件
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
  return func(w http.ResponseWriter, r *http.Request) {
    // 从Authorization头获取token
    authHeader := r.Header.Get("Authorization")
    if authHeader == "" {
      w.WriteHeader(http.StatusUnauthorized)
      fmt.Fprint(w, "Missing authorization header")
      return
    }
    // 验证Bearer格式
    tokenString := authHeader[len("Bearer "):]
    claims := &Claims{}
    // 解析并验证Token
    token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
      return jwtKey, nil
    })
    if err != nil || !token.Valid {
      w.WriteHeader(http.StatusUnauthorized)
      fmt.Fprintf(w, "Invalid token: %v", err)
      return
    }
    // 将声明信息存入请求上下文
    ctx := context.WithValue(r.Context(), "claims", claims)
    next.ServeHTTP(w, r.WithContext(ctx))
  }
}

image.gif

4、获取令牌信息

func protectedHandler(w http.ResponseWriter, r *http.Request) {
  // 从上下文中获取用户名
  claims, ok := r.Context().Value("claims").(*Claims)
  if !ok {
    w.WriteHeader(http.StatusUnauthorized)
    return
  }
  fmt.Fprintf(w, "欢迎访问受保护页面, %s!", claims.Username)
}

image.gif

5、注册路由

func main() {
  // 注册路由
  http.HandleFunc("/login", loginHandler)
  http.HandleFunc("/protected", authMiddleware(protectedHandler))
  fmt.Println("http://localhost:8080//login")
  http.ListenAndServe(":8080", nil)
}

image.gif

四、替换方案

大多技术,并非不可替代。一招吃遍天下鲜,往往是不妥当的

在什么样的场景更适合用哪一种技术这样的眼光看待问题,将会更加妥当。

image.gif 编辑

下方是我搜集来的资料,可做了解

OAuth2.0:

OAuth 2.0 优点:支持第三方授权,保护用户隐私安全;有多种授权模式,适配不同场景,灵活度高;互联网广泛应用,大厂支持,便于开发第三方登录。

OAuth 2.0 缺点:协议复杂,开发者难理解实现,易安全配置不当;授权服务器防护差易致令牌泄露,不同模式也有安全风险 。

SAML:

SAML 优点:支持企业级 SSO,跨安全域传递身份权限,实现单点登录;是开放标准协议,兼容性、互操作性好;支持数字签名与加密,保障身份信息安全。

SAML 缺点:基于 XML,消息冗长,配置维护复杂,需专业知识;XML 解析开销大,大量请求影响性能;企业部署要调整网络、安全策略,成本高 。


借鉴资料:

1、jwt官网

2、JSON Web Token 入门教程



目录
相关文章
|
敏捷开发 测试技术 持续交付
Scrum敏捷开发:适应变化的核心能力
敏捷开发是一种以人为核心,迭代、增量式的软件开发方法。它强调团队成员的密切合作、快速响应需求变化、持续交付高质量软件。
|
JSON 前端开发 网络协议
前端知识点-----跨域
前端知识点-----跨域
443 0
|
人工智能
【Mixup】探索数据增强技术:深入了解Mixup操作
【Mixup】探索数据增强技术:深入了解Mixup操作
1526 0
|
1月前
|
Arthas 人工智能 Java
我们做了比你更懂 Java 的 AI-Agent -- Arthas Agent
Arthas Agent 是基于阿里开源Java诊断工具Arthas的AI智能助手,支持自然语言提问,自动匹配排障技能、生成安全可控命令、循证推进并输出结构化报告,大幅降低线上问题定位门槛。
879 64
我们做了比你更懂 Java 的 AI-Agent -- Arthas Agent
Pycharm配置镜像源(长期有效)
Pycharm配置镜像源(长期有效)
Pycharm配置镜像源(长期有效)
|
存储 缓存 Dart
Flutter&鸿蒙next 封装 Dio 网络请求详解:登录身份验证与免登录缓存
本文详细介绍了如何在 Flutter 中使用 Dio 封装网络请求,实现用户登录身份验证及免登录缓存功能。首先在 `pubspec.yaml` 中添加 Dio 和 `shared_preferences` 依赖,然后创建 `NetworkService` 类封装 Dio 的功能,包括请求拦截、响应拦截、Token 存储和登录请求。最后,通过一个登录界面示例展示了如何在实际应用中使用 `NetworkService` 进行身份验证。希望本文能帮助你在 Flutter 中更好地处理网络请求和用户认证。
830 1
|
测试技术
Appscan手工探索、手工测试功能实战
Appscan手工探索、手工测试功能实战
|
XML 关系型数据库 MySQL
工作流框架--Activiti6.0(二)
工作流框架--Activiti6.0(二)
1090 1
|
关系型数据库 MySQL BI
帆软Report 填报
帆软Report 填报
763 0
|
JSON API 网络架构
Django REST framework视图集与路由详解:深入理解ViewSet、ModelViewSet与路由映射器
Django REST framework视图集与路由详解:深入理解ViewSet、ModelViewSet与路由映射器