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

JWT 实战:在 Spring Boot 中的使用

文章目录

  • 一、JWT简介
  • 二、JWT 的结构
  • 三、JWT 的生成过程
  • 四、JWT 验证过程
  • 五、JWT 的应用场景
  • 六、JWT的实现
    • 6.1 登录接口
    • 6.2 校验 Token 接口
    • 6.3 `jwtUtil` 类
  • 七、总结

一、JWT简介

JWT(JSON Web Token)是一种用于客户端和服务器之间安全传输信息的开放标准(RFC 7519)。JWT 的目的是通过一种 compact、URL-safe 的方式传递信息,以便在 web 应用中广泛使用,尤其是在用户认证和授权中。

二、JWT 的结构

JWT 由三部分组成,每部分之间用 . 进行分隔,格式为: header.payload.signature

  1. Header(头部):描述 JWT 的元数据,通常包含令牌类型(JWT)和签名算法(例如:HS256RS256),头部通常使用 Base64 编码后成为 JWT 的第一部分。
  2. Payload(负载):包含用户信息或其他声明(Claims)。这里的内容是 JSON 格式的声明,可以是公开的(如 name, exp)或私有的。
  3. Signature(签名):用来验证消息的完整性,防止数据被篡改。它是通过 Header 和 Payload 以及密钥(secret)一起生成的。

三、JWT 的生成过程

JWT 的生成通常会通过以下步骤:

  1. Header:指定签名算法(通常是 HMAC SHA256 或 RSA)。

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    
  2. Payload:包含具体的声明信息,如:

    • sub(Subject):标识该 JWT 的主体(通常是用户的 ID)。
    • exp(Expiration):标识 JWT 的过期时间。
    • iat(Issued At):标识 JWT 的签发时间。
    • iss(Issuer):标识 JWT 的签发者。
    • aud(Audience):指定此 JWT 的接收者。

    示例 Payload:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "iat": 1516239022
    }
    
  3. Signature:使用 Header 和 Payload 以及签名密钥生成签名。

    • 如果使用 HMAC SHA256 算法,则签名生成过程为:
      HMACSHA256(
        base64UrlEncode(header) + "." + base64UrlEncode(payload),
        secret)
      
  4. 最终生成的 JWT:将 Header、Payload 和 Signature 连接起来,以 . 分隔。

    header.payload.signature
    

四、JWT 验证过程

JWT 的验证主要是检查签名是否有效以及 exp(过期时间)等是否符合要求。

  1. 从请求头中提取 JWT。
  2. 使用与生成 JWT 时相同的算法、密钥来验证签名是否有效。
  3. 如果签名有效,检查 JWT 的有效期(exp),以及是否存在其他不符合要求的声明。
  4. 如果有效,服务器允许访问该资源;如果无效,返回错误信息。

五、JWT 的应用场景

  • 认证(Authentication):JWT 最常见的应用场景是身份认证。例如,用户登录时,服务器验证其用户名和密码后生成一个 JWT,并将其返回给客户端。客户端在之后的每次请求中将该 JWT 放入 HTTP 请求头(Authorization)。

  • 授权(Authorization):JWT 也常用于访问控制。例如,通过 JWT 可以携带用户的权限信息,确保用户有权限访问某些资源。

  • 信息交换(Information Exchange):JWT 可以安全地传递信息,因为它是自包含的,信息在签发时已经被加密验证,因此不容易篡改。

六、JWT的实现

6.1 登录接口

/login 接口用于用户登录并生成 JWT:

@PostMapping("/login")
public Result login(@RequestBody LoginForm loginForm, HttpSession session){
    // 验证验证码
    String code = (String) this.redisTemplate.opsForValue().get(loginForm.getUuid());
    if(code == null || !code.equals(loginForm.getCaptcha())) {
        return Result.error("验证码错误");
    }
    
    // 验证用户名和密码
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("username", loginForm.getUsername());
    User user = this.userService.getOne(queryWrapper);
    if(user == null || !SecureUtil.sha256(loginForm.getPassword()).equals(user.getPassword())) {
        return Result.error("用户名或密码错误");
    }

    // 检查用户是否被锁定
    if(user.getStatus() == 0) {
        return Result.error("账号已被锁定,请联系管理员");
    }

    // 登录成功,生成 Token
    session.setAttribute("user", user);
    String token = this.jwtUtil.createToken(String.valueOf(user.getUserId()));
    this.redisTemplate.opsForValue().set("communityuser-" + user.getUserId(), token, jwtUtil.getExpire());

    Map<String, Object> map = new HashMap<>();
    map.put("token", token);
    map.put("expire", jwtUtil.getExpire());

    // 返回生成的 token
    return Result.ok().put("data", map);
}

如果登录成功,生成一个 JWT Token,并存储到 Redis 中。返回给客户端该 token 和过期时间。

6.2 校验 Token 接口

/checkToken 接口用于验证客户端传来的 token 是否有效:

@GetMapping("/checkToken")
public Result checkToken(HttpServletRequest request){
    String token = request.getHeader("token");
    boolean result = this.jwtUtil.checkToken(token);
    if(result) return Result.ok().put("status", "ok");
    return Result.ok().put("status", "error");
}
  1. 提取 Token:从请求头中提取 token
  2. 验证 Token:调用 jwtUtil.checkToken 方法来验证 token 是否有效。
  3. 返回验证结果:如果 token 有效,返回 "status": "ok";如果无效,返回 "status": "error"

6.3 jwtUtil

jwtUtil 类封装了生成和验证 JWT 的操作,通常包括以下方法:

  1. createToken:生成一个新的 JWT,包含用户信息和过期时间。

    public String createToken(String userId) {
        return Jwts.builder()
            .setSubject(userId)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))  // 设置过期时间
            .signWith(SignatureAlgorithm.HS256, secretKey)  // 使用密钥进行签名
            .compact();
    }
    
  2. checkToken:验证传入的 token 是否有效。

    public boolean checkToken(String token) {
        try {
            Jws<Claims> claimsJws = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token);  // 解析并验证签名
            return true;
        } catch (Exception e) {
            return false;  // 验证失败
        }
    }
    

七、总结

  • JWT 用于用户认证和授权,包含三部分:头部、负载和签名。
  • 登录流程:通过验证用户名、密码和验证码,生成 JWT Token,并通过 Redis 存储用户的 token
  • Token 校验:通过 jwtUtil.checkToken 方法验证客户端提供的 token 是否有效。
  • 安全性:JWT 通过密钥加密生成和验证签名,确保信息传输的安全性。

JWT 的优势在于无状态认证、跨域认证、以及扩展性强,但也需要注意密钥管理、过期时间控制等安全细节。
在这里插入图片描述


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

相关文章:

  • 后端面试题分享第一弹(状态码、进程线程、TCPUDP)
  • 设计模式之工厂模式
  • 99.17 金融难点通俗解释:归母净利润
  • 【ARM】解决MDK在打开工程的时候提示CMSIS的版本不对问题
  • 图漾相机搭配VisionPro使用简易教程
  • ui-automator定位官网文档下载及使用
  • 网络模型简介:OSI七层模型与TCP/IP模型
  • Learning Vue 读书笔记 Chapter 2
  • 【React+ts】 react项目中引入bootstrap、ts中的接口
  • JavaScript使用toFixed保留一位小数的踩坑记录:TypeError: xxx.toFixed is not a function
  • vue3中customRef的用法以及使用场景
  • LeetCode题练习与总结:两个字符串的删除操作--583
  • 9.4 GPT Action 开发实践:从设计到实现的实战指南
  • PoolingHttpClient试验
  • 独立游戏开发赚钱吗?
  • 从0到1:C++ 开启游戏开发奇幻之旅(一)
  • 重构(1)if-else
  • webview_flutter_android 4.3.0使用
  • java 字符串日期字段格式化前端显示
  • 并发操作下如何加锁,自动释放锁,异常情况可以主动释放锁
  • gitee——报错修改本地密码
  • 51单片机开发:独立键盘实验
  • 数据结构:log-structed结构MemTableSSTable
  • 代码工艺:实践 Spring Boot TDD 测试驱动开发
  • C#常考随笔2:函数中多次使用string的+=处理,为什么会产生大量内存垃圾(垃圾碎片),有什么好的方法可以解决?
  • SocketCAN