JWT令牌:实现安全会话跟踪与登录认证的利器

发布于:2025-04-16 ⋅ 阅读:(31) ⋅ 点赞:(0)

摘要:本文深入探讨了JWT令牌在实现会话跟踪和登录认证方面的应用,详细介绍了JWT令牌的概念、组成、生成与校验方法,以及在实际案例中如何通过JWT令牌进行会话跟踪和登录认证的具体实现步骤,为系统的安全认证机制提供了全面且深入的技术指导。

关键词:JWT令牌;会话跟踪;登录认证;生成与校验
参考资料黑马程序员day12 完整项目请从第10天开始看

一、引言

在基于令牌技术实现会话追踪的背景下,本文着重介绍功能强大的JWT令牌,它作为用户身份标识,以字符串形式存在。接下来将详细阐述JWT令牌的相关知识及在项目中的具体应用。

二、JWT令牌

2.1 介绍

JWT,全称JSON Web Token,官网为https://jwt.io/ 。它定义了一种简洁且自包含的格式,用于在通信双方以json数据格式安全传输信息,凭借数字签名确保信息可靠。

  • 简洁性:JWT表现为简单字符串,可在请求参数或请求头中直接传递。
  • 自包含性:虽看似随机字符串,但可按需求在其中存储自定义数据,如用户相关信息。本质上,JWT是对原始json数据格式进行安全封装,实现通信双方的安全信息传输。

JWT令牌由三部分组成,各部分间以英文点号分隔:

  • Header(头):记录令牌类型、签名算法等信息,例如:{“alg”:“HS256”,“type”:“JWT”} 。
  • Payload(有效载荷):携带自定义信息、默认信息等,例如:{“id”:“1”,“username”:“Tom”} 。
  • Signature(签名):用于防止Token被篡改,确保安全性。它通过将header、payload与指定秘钥结合,运用指定签名算法计算得出。签名机制使得JWT令牌极为安全可靠,一旦令牌中任何部分被篡改,校验时便会失败。

生成JWT令牌时,会对JSON格式数据进行base64编码。Base64是基于64个可打印字符表示二进制数据的编码方式,使用的字符包括A - Z、a - z、0 - 9、加号、斜杠,共64个字符,另加等号作为补位符号。需注意,Base64是编码方式,并非加密方式。

JWT令牌典型应用场景为登录认证,流程如下:

  1. 浏览器发起登录请求,访问登录接口,若登录成功,生成JWT令牌并返回给前端。
  2. 前端接收JWT令牌后存储,后续每次请求将其携带至服务端
  3. 服务端统一拦截请求,判断是否携带令牌。若无令牌,拒绝访问;若有令牌,校验其有效性,有效则放行处理请求。

2.2 生成和校验

为在Java代码中生成和校验JWT令牌,首先需引入JWT依赖:

<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

引入依赖后,可借助工具类Jwts提供的API完成操作。

  • 生成JWT代码实现
@Test
public void genJwt(){
    Map<String,Object> claims = new HashMap<>();
    claims.put("id",1);
    claims.put("username","Tom");

    String jwt = Jwts.builder()
      .setClaims(claims) //自定义内容(载荷)          
      .signWith(SignatureAlgorithm.HS256, "itheima") //签名算法        
      .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期   
      .compact();

    System.out.println(jwt);
}

运行上述测试方法,输出的结果即为生成的JWT令牌。可将令牌复制至JWT官网,粘贴于Encoded位置,自动解析令牌。解析后,第一部分显示所用签名算法为HS256;第二部分为自定义数据及设置的过期时间(exp),因前两部分采用base64编码,可直接解码;第三部分由签名算法计算得出,无法直接解析。https://jwt.io/#debugger-io
在这里插入图片描述

校验JWT令牌(解析生成的令牌)

@Test
public void parseJwt(){
    Claims claims = Jwts.parser()
      .setSigningKey("itheima")//指定签名密钥(必须保证和生成令牌时使用相同的签名密钥)  
      .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk")
      .getBody();

    System.out.println(claims);
}

运行该测试方法,若解析过程未报错,说明解析成功,可看到解析出的id和过期时间。若篡改令牌(如修改header中的数字),解析时会报错,表明JWT令牌被篡改。此外,修改生成令牌时设置的过期时间,过期后再解析也会报错,说明JWT令牌过期后失效。

使用JWT令牌时需注意:

  • JWT校验所用签名秘钥必须与生成JWT令牌时的秘钥配套。
  • 若JWT令牌解析校验报错,则表明令牌被篡改或已失效,即令牌非法。

2.3 登录下发令牌

完成JWT令牌生成和校验的基础学习后,着手在案例中运用JWT令牌技术跟踪会话,主要包含生成令牌和校验令牌两步。首先实现登录成功后生成JWT令牌并返回给前端。

查看登录接口文档的响应数据部分可知,登录成功后系统下发JWT令牌,后续请求需在请求头header中以“token”为名称,携带登录时下发的JWT令牌。若检测到用户未登录,返回固定错误信息。

实现步骤

  • 引入JWT工具类:在项目工程下创建com.itheima.utils包,并将提供的JWT工具类复制到该包下。
  • 登录完成后,调用工具类生成JWT令牌并返回

JWT工具类

public class JwtUtils {

    private static String signKey = "itheima";//签名密钥
    private static Long expire = 43200000L; //有效时间

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)//自定义信息(有效载荷)
                .signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部)
                .setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)//指定签名密钥
                .parseClaimsJws(jwt)//指定令牌Token
                .getBody();
        return claims;
    }
}

登录成功,生成JWT令牌并返回

@RestController
@Slf4j
public class LoginController {
    //依赖业务层对象
    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        //调用业务层:登录功能
        Emp loginEmp = empService.login(emp);

        //判断:登录用户是否存在
        if(loginEmp !=null ){
            //自定义信息
            Map<String , Object> claims = new HashMap<>();
            claims.put("id", loginEmp.getId());
            claims.put("username",loginEmp.getUsername());
            claims.put("name",loginEmp.getName());

            //使用JWT工具类,生成身份令牌
            String token = JwtUtils.generateJwt(claims);
            return Result.success(token);
        }
        return Result.error("用户名或密码错误");
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到