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

JWT概念及JAVA使用

前言

  • JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案

认证方式

认证方式一般有两种

  1. session认证
  2. JWT认证

一、session认证

1、步骤

过去,我们都是使用session认证,步骤如下

  1. 前端访问登录接口
  2. 登录接口验证完账号密码没问题时,在redis中插入一条数据session_id:用户信息,表示这个session_id登录了,然后将这个session_id`返回给前端
  3. 前端接收到session_id后,放入Cookie中,在后续请求接口中自动带上。
  4. 后续接口在收到Cookie时,拿着session_id去查询Redis,获得用户登录身份
    1. 未查询到:返回前端一个状态码,前端跳转到登录页
    2. 查到了:执行正常逻辑,并且返回给前端数据
  5. 登出时:可以让后端将这个session_idredis中删了即可

在这里插入图片描述

2、优劣势

  • 优势:无
  • 劣势:
    1. 如果用户数据量大,那么内存压力也会相应变大
    2. 如果多个微服务,那么需要远程调用登录服务校验,或者每个服务都需要连接这个Redis,并且重复编写请求拦截校验代码

二、JWT认证

1、步骤

  1. 用户访问登录接口
  2. 登录成功后,后端根据JWT提供的方法,设置Header(头部),payload(负载),Signature(签名)
  3. 将生成的结果返回给前端,前端保存到sessionStorage/localStorage中,每次请求带上这个值,放入header
  4. 后端接受到后,直接使用JWT提供的方法,校验根据.签名两个值生成的签名与生成签名时的密钥是否一致

2、生成签名公式

  • Header:有令牌的类型和所使用的签名算法,如HMAC、SHA256、RSA;使用Base64编码组成;(Base64是一种编码,不是一种加密过程,可以被翻译成原来的样子)

    Base64URL({
        "alg":"HS256",
        "typ":"JWT"
        
    })
    
  • Payload:有效负载,包含声明;声明是有关实体(通常是用户)和其他数据的声明,不放用户敏感的信息,如密码。同样使用Base64编码

    Base64URL({
        "id":1,
        "name":"张三",
        "username":"zhangSan"
    })
    
  • Signature:前面两部分都使用Base64进行编码,前端可以解开知道里面的信息。Signature需要使用编码后的header和payload
    加上我们提供的一个密钥,使用header中指定的签名算法(HS256)进行签名。签名的作用是保证JWT没有被篡改过

    HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret(密钥,只有服务器知道))
    
  • 签名:签名的过程实际上是对头部以及负载内容进行签名,防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的

3、代码实践

  • 我这里将JWT所有操作生成Token,校验Token,获取Payload中信息统一提取到JwtTokenUtil类中
package com.tcc.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.sun.org.apache.xml.internal.security.algorithms.SignatureAlgorithm;

import java.util.Date;

public class JwtTokenUtil {
    // 密钥
    private static final String SECRET = "123456";
    // 一天毫秒数
    private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000;

    /**
     * 获取Token
     * @Author: tcc
     * @Date: 2025/3/1 16:17
     */
    public static String getToken() {
        return  JWT.create()
                .withClaim("id", "1") // Payload 用户信息
                .withClaim("name", "张三") // Payload 用户信息
                .withClaim("username", "zhangSan")
                .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRE_TIME)) // 过期时间,一天
                .sign(Algorithm.HMAC256(SECRET));
    }

    /**
     * 校验Token
     * @Author: tcc
     * @Date: 2025/3/1 16:21
     */
    public static void validateToken(String token) {
        try {
            JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
            System.out.println("验证成功");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("验证失败");
        }

        // ExpiredJwtException:当 JWT 的过期时间(exp 声明)已过,验证时就会抛出此异常
        // SignatureException:当 JWT 的签名验证失败时,会抛出该异常。这可能是因为 token 被篡改,或者使用了错误的密钥进行验证。
        // MalformedJwtException 当 JWT 的格式不正确时,例如 token 不是一个有效的 JWT 字符串,或者不满足 JWT 的结构(由头部、载荷和签名三部分组成,用 . 分隔),就会抛出此异常。
        // UnsupportedJwtException:当 JWT 使用了不支持的签名算法,或者 token 包含不支持的类型、声明等时,会抛出此异常
        // IllegalArgumentException: 当 token 为 null 或者为空字符串时,就会抛出此异常
    }

    /**
     * 获取 payload 信息
     * @Author: tcc
     * @Date: 2025/3/1 16:23
     */
    public static void getPayload(String token) {
        DecodedJWT verify = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
        System.out.println(verify.getExpiresAt()); // 获取 过期时间
        System.out.println();
        verify.getClaims().forEach((key, value) -> {
            System.out.println(key + ":" + value.asString());
        });
    }
}

测试代码

package com.tcc.controller;

import com.tcc.utils.JwtTokenUtil;
import org.junit.jupiter.api.Test;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @Test
    void login() {
        // ------- 验证账号密码 省略 -------
        // 通过 每次都会随机生成,但是每个都可以校验成功
        System.out.println(JwtTokenUtil.getToken());
    }

    @Test
    void validateToken() {
        // 根据上面token复制而来
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi5byg5LiJIiwiaWQiOiIxIiwiZXhwIjoxNzQwOTA0NjgyLCJ1c2VybmFtZSI6InpoYW5nU2FuIn0.8XickAlHQI14v9SE-z_GDDpr5Oed8fVcuw4UAetccdY";
        JwtTokenUtil.validateToken(token);
    }

    @Test
    void getPayload() {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi5byg5LiJIiwiaWQiOiIxIiwiZXhwIjoxNzQwOTA0NjgyLCJ1c2VybmFtZSI6InpoYW5nU2FuIn0.8XickAlHQI14v9SE-z_GDDpr5Oed8fVcuw4UAetccdY";
        JwtTokenUtil.getPayload(token);
    }
}

效果

在这里插入图片描述

三、总结

  • JWT默认是不加密的payload里面内容也是根据Base64URL编码转化了一下,并没有加密,所以不能存放密码等信息,当然也可以在生成原始Token的时候再加密一次
  • JWT最大的缺点是:由于服务器不保存session状态,当前端登出操作时,无法废除Token,也就是说,一旦Token签发了,再到期之前就会始终有效
    • 解决办法:
      1. 登出时,前端删除Token信息即可
      2. 登陆成功后返回前端时间较短的accessToken,以及一个时间较长的refreshToken(用来重新获取token),前端清除refreshToken后则无法获取新token
  • JWT本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

http://www.kler.cn/a/567561.html

相关文章:

  • Winbox5怎样设置上网
  • PAT乙级(1026 程序运行时间)C语言超详细解析
  • 使用Spring Boot与达梦数据库(DM)进行多数据源配置及MyBatis Plus集成
  • seacms v9 实现的MySQL注入
  • Kubernetes 详解笔记
  • Word快速替换修改学术论文所有中的中括号引用未上标格式
  • BiliBili视频下载-原理与实现Python+FFmpeg
  • 【Netty】五种经典 IO 模型详解,附各种模型流程图及流程详解
  • 特征值与特征向量
  • 【GraphQL API 漏洞简介】
  • vue3+TS使用i18n.global.locale,页面要刷新时才更新
  • 第十四届蓝桥杯:DFS之飞机降落
  • 如何在VUE框架下渲染出来一个水球图
  • 建筑兔零基础人工智能自学记录34|深度学习与神经网络2
  • AI时代保护自己的隐私
  • 【多模态大模型】阶跃星辰放大招Step-Audio:统一语音理解与生成的智能语音交互模型
  • Java在物联网(IoT)中的应用研究
  • 250301-OpenWebUI配置DeepSeek-火山方舟+硅基流动+联网搜索+推理显示
  • 【实战 ES】实战 Elasticsearch:快速上手与深度实践-1.3.2Kibana可视化初探
  • Linux----网络通信