jwt——json web token介绍

    科技2025-12-16  19

    https://www.bilibili.com/video/BV1i54y1m7cP

    文章目录

    1. 什么是JWT2. jwt能做什么3. 为什么是JWT传统的基于session认证jwt认证流程jwt组成优缺点优点:缺点: demo整合spring boot封装工具类JWTUtils

    1. 什么是JWT

    jwt(json web token)是一种开放标准(rfc 7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全的传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名通俗来讲,jwt就是通过JSON形式作为WEB应用中的令牌,用于在各方之间安全的将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。

    2. jwt能做什么

    授权 这是使用jwt最常见的方案。一旦用户登录,每个后续请求将包含JWT,从而允许用户访问该令牌允许的路由,服务和资源。单点登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。信息交换 jwt是在各方之间安全传输信息的好办法。因为可以对JWT进行签名,所以可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此还可以验证内容是否遭到篡改。

    3. 为什么是JWT

    传统的基于session认证

    因为http本身是一种无状态的协议,我们并不能知道是哪一个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器端存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,以便引用识别用户。

    暴露问题:

    每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以便用户下次请求的鉴别,通常而言session是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大用户认证之后,服务端做认证记录。如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力,这也意味着限制了应用的扩展能力。因为是基于cookie来识别用户的,当cookie被截获,用户很容易受到2跨站请求伪造的攻击。在前后端分离系统中就更加痛苦:前后端分离([用户]< == > [前端web] < == > [代理层] < == > [后端应用] )在应用解耦后增加了部署的复杂性。通常用户一次请求就要转发多次。如果用session每次携带sessionid到服务器,服务器还要查询用户信息。同时如果用户很多。这些信息存储在服务器内存中,给服务器增加负担。还有就是CSRF(跨站伪造请求攻击),session是基于cookie进行用户识别的,cookie如果被截获,用户很容易受到跨站请求伪造的攻击。还有就是sessionid是一个特征值,表达的信息不够丰富。不容易扩展。入股后端应用是多节点部署,那么就要实现session共享机制。不方便集群应用。

    jwt认证流程

    首先,前端通过web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密传输(https协议),从而避免敏感信息被嗅探。后端核对用户名和密码成功后,将用户的id等其他信息作为JWT payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个token。后端将jwt字符串作为登陆成功的返回结果返回给前端。前端将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决xss和xsrf问题)后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查token是否过期;检查token的接收方是否是自己(可选)。验证通过后后端使用JWT包含的用户信息进行其它逻辑操作,返回相应结果。

    jwt组成

    jwt分为三个部分

    第一部分 header :标记使用什么算法 (HS256/RSA256) 令牌的类型和所使用的签名算法,例如HMAC SHA256或RSA。{“alg”:“HS256”,“typ”:“JWT”} 第二部分 PayLoad (载荷) jwt存放的数据第三部分 PayLoad 采用MD5加密之后的签名值

    ! jwt不要存放重要数据 Base64.Encode(header). Base64.Encode(PayLoad ).签名值

    private static final String SIGN_KEY = "mayikt"; @Test public void test1(){ JSONObject header = new JSONObject(); header.put("alg","HS256"); JSONObject payLoad = new JSONObject(); payLoad.put("phone","18611011111"); String payLoadStr = payLoad.toJSONString(); //payLoad实现MD5加密 String sign = DigestUtils.md5DigestAsHex((payLoadStr+SIGN_KEY).getBytes()); Base64.Encoder encoder = Base64.getEncoder(); String jwt = (String) encoder.encodeToString(header.toJSONString().getBytes())+"."+ encoder.encodeToString(payLoadStr.getBytes())+"." +sign; System.out.println(jwt);//eyJhbGciOiJIUzI1NiJ9.eyJwaG9uZSI6IjE4NjExMDExMTExIn0=.badce2fbf728c40222e7fa75bcfa9e54 }

    生成的jwt可在https://jwt.io/验证

    验证签名位true的情况下就可以获取payload数据,否则jwt无效。

    优缺点

    优点:

    简洁:数据量小,传输速度快自包含:负载中包含了所有用户所需要的信息,避免了多次查询数据库jwt客户端就可验证,减轻服务端的压力,适合分布式微服务以JSON加密的形式保存在客户端,跨语言,原则上任何web形式都支持。jwt查询效率比token高不容易被客户端篡改数据

    缺点:

    一旦生成好一个jwt,后期比较难使它失效如果payload数据过多,占用服务器带宽资源。

    建议在jwt的payload数据中增加时间戳,设置有效期

    demo

    引入依赖 com.auth0 java-jwt <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.11.0</version> </dependency> 生成token: //生成时间戳 Calendar instance = Calendar.getInstance(); instance.add(Calendar.SECOND,90); //生成令牌 String token = JWT.create() .withClaim("user","yz") .withExpiresAt(instance.getTime()) //设置过期时间 .sign(Algorithm.HMAC256("token!Q2W#E$RW"));//设置签名 保密 复杂 //输出令牌 System.out.println(token);//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDIxNzg3OTYsInVzZXIiOiJ5eiJ9.PavrIse8cAsM3bW4roGgkY8kHTAqBwiV8kZRT1K4eu8 解析结果: header:{ "typ": "JWT", "alg": "HS256" } payload: { "exp": 1602178796, "user": "yz" } 根据令牌和签名解析数据 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("token!Q2W#E$RW")).build(); DecodedJWT decodedJWT = jwtVerifier.verify(token); System.out.println("user: " + decodedJWT.getClaim("user").asString()); //新增.withClaim("id",1) System.out.println("id: " + decodedJWT.getClaim("id").asInt()); System.out.println("过期时间:" + decodedJWT.getExpiresAt());

    整合spring boot

    封装工具类JWTUtils

    方法 getToken 传入Map<String,Object> map foreach map -> withClaim

    验证token方法 verify

    获取token信息方法:getTokenInfo(String token,String key) 或直接返回DecodedJWT

    连接数据库

    Processed: 0.018, SQL: 9