☰
JWT(1)介绍和原理
2026/10/29 10:14:50 网站建设 项目流程

目录

一、介绍

1、简介

2、与session相对比较

3、流程:

4、安全问题:

5、失效问题

6、在线解析平台

二、结构

1、Header

1.1、介绍

1.2、组成

2、Payload

3、Signature

3.1、介绍

3.2、加密算法

3.3、使用建议

三、api操作

1、sign生成

2、verify验证

3、获取payload


一、介绍

1、简介

JWT无状态登录,是token的一种。全称json web token轻量级的授权和身份认证规范。跨平台存在。结构是 JSON,它是一个 JavaScript 对象表示法。它有各种部分,标头、有效负载和签名部分。这些都被打包起来,然后对 Base64 进行编码和签名,以便它是 URL 安全的。

2、与session相对比较

(1)有状态登录session:tomcat中,把用户的登录信息保存在服务端的session中,再给用户一个cookie值。记录对应的session值,下次客户端带cookie来访问时,再识别对应的session找到用户的信息。缺点:服务器的负荷变大,保存大量的session数据。

(2)无状态登录jwt:服务器不保存用户的登录信息,由客户端请求携带自己的身份信息(带入token来获取身份认证)。服务器不再保存用户的登录信息,多次请求不用访问回到同一台服务,服务器可以弹性伸缩,减小服务端压力,缺点一旦JWT签发,在有效期内这个token将会一直有效(和服务是否重启无关),而session有自己的会话周期。

3、流程:
  • 第一次请求访问时候,由服务器对用户进行身份验证
  • 验证通过,将身份信息加密进程token,返回给客户端,作为其访问其他服务器集群的身份凭证。
  • 每次请求携带token,服务器对其进行解密判断有效性。

4、安全问题:

token必须安全可靠,推荐采用JWT + RSA非对称加密,另外,JWT一旦签发,在有效期内将会一直有效,可以通过设置较短过期时间,或在业务接口再次做判断、再加入拦截等方法来减小安全风险。

(1)Base64编码方式是可逆的,也就是透过编码后发放的Token内容是可以被解析的。一般而言,我们都不建议在有效载荷内放敏感讯息,比如使用者的密码。

(2)JWT其中的一个组成内容为Signature,可以防止通过Base64可逆方法回推有效载荷内容并将其修改。因为Signature是经由Header跟Payload一起Base64组成的。

(3) Cookie 被窃取了第三方可以做 CSRF 攻击:Cookie丢失,就表示身份就可以被伪造。故官方建议的使用方式是存放在LocalStorage中,并放在请求头中发送。

5、失效问题
  • 无状态JWT令牌(Stateless JWT Token)发放出去之后,不能通过服务器端让令牌失效,必须等到过期时间过才会失去效用。
  • 假设在这之间Token被拦截,或者有权限管理身份的差异造成授权Scope修改,都不能阻止发出去的Token失效并要求使用者重新请求新的Token。
6、在线解析平台

New tab (jwt.io)

二、结构

在紧凑的形式中,JWT包含三个由点(.)分隔的部分,它们分别是:header、payload、sign。

如我生成了一个jwt token:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJ1c2VybmFtZTEiOiJscyIsImV4cCI6MTcyMDE2NjY4NCwidXNlcm5hbWUiOiJscyJ9.QZpJ0mMjm7PmXFb682Xnp5KhaUp8gQ50g-rOU8nvYow
1、Header
1.1、介绍

第一部分是header。为base64编码,可解码,不要存放关键信息。如我这里解码得到:

或者通过jwt在线解析平台:

1.2、组成

Header通常由两部分组成:

(1)令牌的类型(即JWT);

(2)常用的加密算法,见

com.auth0.jwt.algorithms.Algorithm
2、Payload

第二部分是包含具体的身份信息和声明 (claims)的载荷。为base64编码,可解码。

放声明内容,就是存放沟通讯息的地方,在定义上有3种声明(Claims):

(1)Registered claims(注册声明):
这些是一组预先定义的声明,它们不是强制性的,但推荐使用,以提供一组有用的,可互操作的声明。 其中一些是:iss(发行者),exp(到期时间),sub(主题),aud(受众)等。#Registered Claim Names#

(2)Public claims(公开声明):
这些可以由使用JWT的人员随意定义。 但为避免冲突,应在IANA JSON Web令牌注册表中定义它们,或将其定义为包含防冲突命名空间的URI。
(3)Private claims(私有声明):
这些是为了同意使用它们但是既没有登记,也没有公开声明的各方之间共享信息,而创建的定制声明。

如我这里解码得到:

通过jwt在线解析平台:

3、Signature
3.1、介绍

签名,由前两部分加密形成,用于验证 Token 的完整性和真实性,不能base64解码。

如我使用一个sign通过base64解码不了

3.2、加密算法

Signature不能被解密。因为 Signature 并不是加密的结果,而是使用指定的加密算法(如 HMAC、RSA 等)基于 Header 和 Payload 计算出来的一个摘要或签名,相当于先加密再hash。Signature 的作用是用于验证 JWT 是否被篡改过,而不是用来获取原始数据或加密的数据。

计算方法:

Signature是这么算出来的? var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, 'your_secret'); 说明: 1、header和payload先用base64加密用点链接起来,再用header里alg所指定的算法计算摘要 2、your_secret 就是秘钥,加密方和解密方共同知道的,解密的时候要用

如:使用HMAC SHA256算法,签名将按照以下方式创建:

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

通过在线解析平台:

3.3、使用建议

不要存放敏感信息在Token里。
Payload中的exp时效不要设定太长。
开启Only Http预防XSS攻击。
如果担心重播攻击(replay attacks )可以增加jti(JWT ID),exp(有效时间) Claim。
在你的应用程序应用层中增加黑名单机制,必要的时候可以进行Block做阻挡(这是针对掉令牌被第三方使用窃取的手动防御)。

三、使用方法

以常见的auth0和jjwt插件为例:

1、sign生成

需要指定算法,算法又依赖了secret,所以需要保管好secret防止别人随意生成来攻击系统

(1)auth0
JWT.create() //iss:签发方 .withIssuer("wtyy") //aud:接收jwt的一方 .withAudience("web-demo") //exp:jwt的过期时间 //.withExpiresAt() //其他自定义通信信息 .withClaim("username", userEntity.getUsername()) .withClaim("age",userEntity.getAge()) .withExpiresAt(date) .sign(algorithm);
(2)jjwt
// 生成 JWT public static String generateToken(String key) { long expirationTime = 1000 * 60 * 60; // 1小时 Date now = new Date(); Date expiryDate = new Date(now.getTime() + expirationTime); return Jwts.builder() .setSubject("web") .setIssuer("wtyy") .setAudience("to_third_party") .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS256, key) .claim("userName","wtyy") .claim("age",18) .compact(); }
2、获取payload

注意,只要是一个jwt,都可以解析出payload(即使过期了也可以解析出payload,因为过期是业务判断的)。所以拿到一个jwt,应该:

① 验证算法是否合法,即是不是签发方生成的

② 并且判断是不是已经过期了;

③ 最后再去获取payload;

④ 如果需要,获取payload后还可以通过payload通信信息做进一步的校验,从而判断token是否合法。

(1)auth0

对于第一步,auth0提供了单独的 verify验证方法:验证不通过会抛异常

private static boolean verifyToken(String secret, String token) { Algorithm algorithm = getCurrentAlgorithm(secret); try{ JWTVerifier verifier = JWT.require(algorithm).build(); //验证不通过会抛异常 verifier.verify(token); return true; }catch (Exception e){ log.error("token非法"); return false; } }

完整的验证,关键代码示例:

DecodedJWT jwt = JWT.decode(token); //1、验证 是否过期 Date expireDate = jwt.getExpiresAt(); if(expireDate.before(new Date())){ log.error("token已过期"); } //2、verify验证 verify(); //3、获取通信字段 Map<String, Claim> claims = jwt.getClaims(); String username = claims.get("username").asString(); int age = claims.get("age").asInt(); //4、根据通信字段进一步校验是否合法;合法的存入全局变量做为当前调用方的凭证 ......
(2)jjwt

jjwt插件的API设计,获取payload可传入密钥来验证算法,验证不通过会抛异常,验证成功返回payload

package org.es.demo.util; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import lombok.extern.slf4j.Slf4j; import java.util.Date; @Slf4j public class JwtUtil { //private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256); // 自动生成一个符合要求的密钥 // 生成 JWT public static String generateToken(String key) { long expirationTime = 1000 * 60 * 60; // 1小时 Date now = new Date(); Date expiryDate = new Date(now.getTime() + expirationTime); return Jwts.builder() .setSubject("web") .setIssuer("wtyy") .setAudience("to_third_party") .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS256, key) .claim("userName","wtyy") .claim("age",18) .compact(); } private static String getUserNameByToken(String token, String key) { Claims claims; try{ claims = Jwts.parser() .setSigningKey(key) // 设置签名密钥 .parseClaimsJws(token) .getBody(); // 获取 Claims 数据 }catch (Exception e){ log.error("token不合法"); return null; } Date expirationDate = claims.getExpiration(); boolean isExpired = expirationDate.before(new Date()); if (isExpired) { log.error("token已过期"); return null; } return (String) claims.get("userName"); } public static void main(String[] args) { String key = "******"; String jwt = generateToken(key); String userName = getUserNameByToken(jwt,key); log.info("结果:"+userName); } }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询