Jwt学习-cnblog

Jwt学习

Jwt是Json web token的缩写,这个技术主要是用来进行身份认证,实现单点登录。

1.Jwt和Session/cookie的比较

相比session和cookie,Jwt的优势:

  • 无状态:服务端不需要存储大量用户token,即无状态,服务端不需要存储会话状态,只需要在服务端设置一个密钥即可实现;
  • 分布式:可以支持分布式,因为这个是无状态的,服务端并不存登录的会状态,那么用户可以实现在多端登录,如果是session,服务端之间需要同步会话状态,显然是不优的;
  • 跨域/跨服务:对于不同的端之间使用也没有影响,比如移动端等;
  • 自包含:载荷携带信息,不需要每次查库;

缺点:

  • 不能像session一样,可以支持删除,jwt只能设置一个过期时间exp;
  • jwt体积比SessionId大,每次请求携带消耗流量;
  • Payload可解码,不能存敏感信息;
  • 过期时间不能太长,否则不安全;
  • 密钥secret绝对不能泄露;

2.引入Maven坐标

		<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>4.4.0</version>
            <scope>compile</scope>
        </dependency>

3.Jwt格式

JWT 是一段由 3 段 Base64 编码字符串 组成的文本,用 . 分隔:

xxxxx.yyyyy.zzzzz

比如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

这三段分别是Header,Payload,Signature。

3.1 Header(头部)

作用:用来描述加密算法、类型。

示例:

{
	"alg": "HS256" //加密算法
	"typ": "JWT" //Json格式的类型,还有JWE,这是加密令牌,JWT是普通令牌
}

3.2 Payload(载荷)

作用:用来存放会话信息(如 userId、username、过期时间)。

注意:这个是没有加密的,所以一定不能加入密码、手机号等关键信息。

规范写法(推荐):身份唯一 ID 放 sub,额外信息自定义 key:

{
  "iss": "web-login-server",
  "exp": 1788888888,
  "sub": "10001",    // 标准字段存用户唯一ID
  "username": "zhangsan", // 自定义字段
  "role": "admin"         // 自定义字段
}

主要常用到的字段是sub(一般存用户id)、exp(过期时间)。

3.3 Signature(签名)

作用:防止token被修改。

生成规则:

Signature = HMACSHA256(
  Base64(Header) + "." + Base64(Payload),
  密钥(secret)
)

服务端存了加密密钥,如果token被修改,那么签名验证会失败。

4.Jwt认证流程

  1. 前端拿到用户名和密码,发送给后端;
  2. 后端进行判断,如果登录成功,同时生成Jwt返回给前端;
  3. 前端收到Jwt后保存到localstorage/cookie中;
  4. 以后每次请求都携带Jwt;
  5. 服务端对Jwt进行验证,如果验证成功,放心;
  6. 否则非法/过期;

5.实操

package com.alij.javalearning.Jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;


import java.util.Date;

public class JwtTest {
    //密钥
    public static final String secret = "your-secret-key-123456789";

    //生成Token
    public static String createToken(){
        Algorithm algorithm = Algorithm.HMAC256(secret);
        return JWT.create()
                .withClaim("userId",1001)
                .withClaim("username","alij")
                .withExpiresAt(new Date(System.currentTimeMillis() + 3600 * 1000)) //设置过期时间为1小时
                .sign(algorithm);
    }

    //解析token,如果认证通过,会返回一些用户信息等,失败则会抛出一些异常,比如过期。
    public static DecodedJWT verifyToken(String token){
        Algorithm algorithm = Algorithm.HMAC256(secret);
        return JWT.require(algorithm)
                .build()
                .verify(token);
    }

    public static void main(String[] args) {
        String token = createToken();
        System.out.println("生成的Token: " + token);

        try {
            DecodedJWT decodedJWT = verifyToken(token);
            System.out.println("解析后的Token信息: ");
            System.out.println("userId: " + decodedJWT.getClaim("userId").asInt());
            System.out.println("username: " + decodedJWT.getClaim("username").asString());
            System.out.println("过期时间: " + decodedJWT.getExpiresAt());
        } catch (Exception e) {
            System.out.println("Token验证失败: " + e.getMessage());
        }
    }
}

生成的Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEwMDEsInVzZXJuYW1lIjoiYWxpaiIsImV4cCI6MTc4NDM0ODM0OX0.d3ZTasQdNeBwaUfBkk1HPkt59gG5C3l-Ed_72L5VOh0
解析后的Token信息: 
userId: 1001
username: alij
过期时间: Sat Jul 18 12:19:09 CST 2026

6.优化

这里就会有一个问题了,如果过期时间为一天的话,那么过了一天用户就要重新登录,显然对于一些网站上的用户来说是不友好的,我们也经常可以看到登录时有一个选项,保留登录状态多少天,这个是怎么实现的呢?

可以实现一个双token,即用户在登录时,我们给他返回两个token,一个时鉴权token,一个刷新token。

鉴权token一般设置过期时间为15分钟到一个小时。

我们给刷新token一个接口/refresh,当某一次访问业务接口时:

  1. 先被拦截器拦截;
  2. 如果鉴权token没有过期,那么直接放行;
  3. 验证到鉴权token过期,这时候如果刷新token没有过期,那么我们先去访问/refresh接口,重新发布一个鉴权token,之后就可以拿着这个鉴权token去访问业务接口;
  4. 如果鉴权token过期,同时刷新token过期,那么用户就必须重新登录了。
posted @ 2026-07-18 11:28  alij  阅读(5)  评论(0)    收藏  举报