web: jwt令牌构成、创建的基本流程及原理

一、JWT 的构成

1. 概念

json web token(JWT) 本质是一串含义验证信息的字符串,服务器根据JWT和密钥,经过加密算法,验证该字符串是否有效。

2. 构成

JWT由Header、Payload、Signature构成,每个部分都是一个token,如下:

它们的实际含义如下:

        Header:json字符串,指定加密算法(供Signature使用)和类型(一般写死为“JWT”)

        Payload:json字符串,包含通用信息(如发布者iss、发布时间戳iat、过期时间戳exp)和自定义属性(如uid)

        Signature:加密函数,输入“Header”、“Payload”、“密钥”,输出密文

3. 验证方式

STEP 1:客户端登录,传递用户名、密码,服务器端验证资格,成功后生成JWT(Header、Payload、Signature皆由服务器端生成)

STEP 2:服务器端返回JWT给客户端,客户端将JWT存储在本地或浏览器中

STEP 3:客户端请求资源,传递JWT,服务器端获得JWT后,使用Header指定的加密算法,输入“Header"、"Payload"、以及服务器端存储的”密钥“,计算Signature的部分,看看服务器端计算的Signature和客户端传递的Signature是否一致,一致则验证通过。

(图片来自https://www.bilibili.com/video/BV13t5PzDEzh

二、JWT的创建过程

1. 确定 Token 的基本信息(Claims 设计)

JWT 的本质是一个 带签名的 Claims 集合,生成前需明确:

(1)通用声明(Registered Claims)

常见、推荐使用的字段:

Claim含义是否必须
iss签发者(issuer)
sub主题(subject,一般是用户 ID / username)是(推荐)
aud接收方
iat签发时间是(推荐)
exp过期时间是(强烈推荐)
nbf生效时间
jtiJWT 唯一标识
(2)自定义声明(Private Claims)

用于业务识别,例如:

Claim含义是否必须
uid用户id
name用户名
原则:不放敏感信息(如密码)

2. 选择签名算法(Algorithm)

这是 JWT 安全性的核心。

算法类型示例特点
对称加密HS256 / HS512简单、性能高,签发与验证用同一密钥
非对称加密RS256 / ES256私钥签发,公钥验签,更安全

业务系统中最常见:

  • 单体 / 内部系统:HS256
  • 微服务 / 第三方接入:RS256

3. 准备密钥(Secret / Key)

  • HS256:一段足够复杂的字符串(≥ 256 bit)
  • RS256:RSA 私钥(签名)+ 公钥(验证)
密钥应存放在:配置中心 如 application.yml环境变量Vault
❌ 不要硬编码在代码中

如配置在yml中

 jwt: # 设置jwt签名加密时使用的秘钥 admin-secret-key: itcast # 设置jwt过期时间 admin-ttl: 7200000 # 设置前端传递过来的令牌名称 admin-token-name: token user-secret-key: itheima user-ttl: 7200000 user-token-name: authentication

4. 构造 Claims

将步骤 1 中的字段写入 token 负载:

  • 标准声明
  • 自定义业务字段
  • 时间字段统一使用 Date
Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.USER_ID,user.getId()); String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);

5. 生成并签名 JWT

Header + Payload 使用指定算法和密钥进行签名,得到最终的 JWT 字符串

xxxxx.yyyyy.zzzzz 

6. 返回 Token 给客户端

常见返回方式:

JSON 响应体

{ "access_token": "...", "expires_in": 7200 } 

HTTP Header

Authorization: Bearer <jwt> 

三、Spring Boot 中JWT 生成的标准代码结构

代码来自于苍穹外卖,它自己定义了一个JwtUtil类专门处理JWT相关,其中定义了一个createJWT方法,用于创建JWT。

这个例子只是生成JWT的一种,不一定按照这样做

0. 定义createJWT方法,标准化JWT创建流程

苍穹外卖里面既有商家端的员工登录,又有客户端的微信用户登录,都要用到JWT,因此它写了一个通用类来专门处理JWT相关,它的流程是这样的:

1. 传入secretKey(密钥)、ttl(生存时间)、claims(一个HashMap,存储payload部分的信息)

2. 指定加密算法

3. 根据ttl(生存时间) 计算exp(过期时间)

4. 构建JWT,调用官方的Jwts传入 claims、签名(指定的算法、密钥等)、过期时间

package com.sky.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.Map; public class JwtUtil { /** * 生成jwt * 使用Hs256算法, 私匙使用固定秘钥 * * @param secretKey jwt秘钥 * @param ttlMillis jwt过期时间(毫秒) * @param claims 设置的信息 * @return */ public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) { // 指定签名的时候使用的签名算法,也就是header那部分 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 生成JWT的时间 long expMillis = System.currentTimeMillis() + ttlMillis; Date exp = new Date(expMillis); // 设置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的 .setClaims(claims) // 设置签名使用的签名算法和签名使用的秘钥 .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8)) // 设置过期时间 .setExpiration(exp); return builder.compact(); } /** * Token解密 * * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个 * @param token 加密后的token * @return */ public static Claims parseJWT(String secretKey, String token) { // 得到DefaultJwtParser Claims claims = Jwts.parser() // 设置签名的秘钥 .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)) // 设置需要解析的jwt .parseClaimsJws(token).getBody(); return claims; } } 

1. 在application.yml中配置jwt

上面说到secret key 最好放在配置中,不要放在代码里,因此苍穹外卖在yml中配置

sky: jwt: # 设置jwt签名加密时使用的秘钥 admin-secret-key: itcast # 设置jwt过期时间 admin-ttl: 7200000 # 设置前端传递过来的令牌名称 admin-token-name: token user-secret-key: itheima user-ttl: 7200000 user-token-name: authentication

2. 配置JwtProperties,读取application.yml中jwt的相关配置

上面的yml配置的jwt属性,需要通过properties读取到

package com.sky.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "sky.jwt") @Data public class JwtProperties { /** * 管理端员工生成jwt令牌相关配置 */ private String adminSecretKey; private long adminTtl; private String adminTokenName; /** * 用户端微信用户生成jwt令牌相关配置 */ private String userSecretKey; private long userTtl; private String userTokenName; } 

3. 在登录的Controller中生成JWT,返回给用户

基本就是两个步骤

1. 创建claims,是一个hashMap,存入“用户id:xxxx" 的键值对。这个东西用来唯一标识是哪个用户。

2. 创建JWT,传入secretKey(密钥)、ttl(生存时间)、claims。createJWT方法内部再进一步的设置加密算法、

员工登录

package com.sky.controller.admin; import com.sky.constant.JwtClaimsConstant; import com.sky.dto.EmployeeDTO; import com.sky.dto.EmployeeLoginDTO; import com.sky.dto.EmployeePageQueryDTO; import com.sky.entity.Employee; import com.sky.properties.JwtProperties; import com.sky.result.PageResult; import com.sky.result.Result; import com.sky.service.EmployeeService; import com.sky.utils.JwtUtil; import com.sky.vo.EmployeeLoginVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; /** * 员工管理 */ @RestController @RequestMapping("/admin/employee") @Slf4j @Api(tags = "员工相关接口") public class EmployeeController { @Autowired private EmployeeService employeeService; @Autowired private JwtProperties jwtProperties; /** * 登录 * @param employeeLoginDTO * @return */ @PostMapping("/login") @ApiOperation(value = "员工登录") public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) { log.info("员工登录:{}", employeeLoginDTO); Employee employee = employeeService.login(employeeLoginDTO); //登录成功后,生成jwt令牌 Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.EMP_ID, employee.getId()); String token = JwtUtil.createJWT( jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims); EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder() .id(employee.getId()) .userName(employee.getUsername()) .name(employee.getName()) .token(token) .build(); return Result.success(employeeLoginVO); }

微信用户登录

package com.sky.controller.user; import com.sky.constant.JwtClaimsConstant; import com.sky.dto.UserLoginDTO; import com.sky.entity.User; import com.sky.properties.JwtProperties; import com.sky.result.Result; import com.sky.service.UserService; import com.sky.utils.JwtUtil; import com.sky.vo.UserLoginVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/user/user") @Api(tags = "C端用户相关接口") @Slf4j public class UserController { @Autowired private UserService userService; @Autowired private JwtProperties jwtProperties; /** * 微信登录 * @param userLoginDTO * @return */ @PostMapping("/login") @ApiOperation("微信登录") public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){ log.info("微信用户登录:{}",userLoginDTO.getCode()); //微信登录 User user = userService.wxLogin(userLoginDTO); //为微信用户生成jwt令牌 Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.USER_ID,user.getId()); String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims); UserLoginVO userLoginVO = UserLoginVO.builder() .id(user.getId()) .openid(user.getOpenid()) .token(token) .build(); return Result.success(userLoginVO); } } 

四、JWT 与 Spring Security 的关系

  • JWT 本身只负责“凭证”
  • Spring Security 负责:
    • 校验 JWT
    • 解析 Claims
    • 构造 Authentication

JWT ≠ 登录框架,而是 无状态认证载体

Read more

若依(RuoYi)低代码框架全面分析

若依(RuoYi)低代码框架全面分析

文章目录 * 一、框架概述与技术背景 * 技术架构全景 * 二、核心特长分析 * 1. 完备的权限管理体系 * 2. 高度模块化的系统设计 * 3. 强大的代码生成器 * 4. 丰富的功能组件 * 三、显著短板与局限性 * 1. 技术栈相对保守 * 2. 代码生成器的局限性 * 3. 性能瓶颈与扩展性挑战 * 4. 学习曲线与定制成本 * 四、实际应用场景分析 * 适合场景 * 不适用场景 * 五、与其他框架对比 * 六、总结与展望 一、框架概述与技术背景 若依(RuoYi)是基于Spring Boot的权限管理系统,是中国Java低代码领域的代表性开源框架。其名称"若依"取自"若你"的谐音,体现了"

neo4j 5.26版本下载安装配置步骤

安装环境要求 操作系统:Windows 10/8/7、macOS 10.13或更高版本、Linux(Ubuntu、CentOS、Red Hat 等) JDK 17 或更高版本(Neo4j 5.26开始需要JDK 17或更高版本。如果您使用的是较旧的JDK版本,则需要升级到JDK 17或更高版本以运行Neo4j 5.26)64位操作系统 下载Neo4j 由于官方下载速度极慢,我已经把安装文件打包上传到网盘,直接下载即可: 下载地址:https://pan.quark.cn/s/0f2a99911586 下载配置JDK 推荐链接:https://blog.ZEEKLOG.net/ts5218/article/details/135252463 配置环境变量

从零开始:Xilinx FPGA实现RISC-V五级流水线CPU手把手教程

从一块FPGA开始,亲手造一颗CPU:RISC-V五级流水线实战全记录 你还记得第一次点亮LED时的兴奋吗?那种“我真正控制了硬件”的感觉,让人上瘾。但如果你能 自己设计一颗处理器 ,让它跑起第一条指令——那才是数字世界的终极浪漫。 今天,我们就来做这件“疯狂”的事:在一块Xilinx FPGA上,用Verilog从零实现一个 完整的RISC-V五级流水线CPU 。不是调用IP核,不是简化版demo,而是包含取指、译码、执行、访存、写回五大阶段,并解决真实数据冒险与控制冒险的可运行核心。 这不仅是一次教学实验,更是一场对计算机本质的深度探索。 为什么是 RISC-V + FPGA? 别误会,我们不是为了赶潮流才选RISC-V。恰恰相反,它是目前最适合学习CPU设计的指令集。 * 开放免费 :没有授权费,文档齐全,连寄存器编码都写得明明白白。 * 简洁清晰 :RV32I只有40多条指令,没有x86那样层层嵌套的历史包袱。 * 模块化扩展 :基础整数指令够用,后续想加浮点、压缩指令、向量扩展,都可以一步步来。

【具身智能】机器人训练流程

机器人训练是一个涵盖硬件和软件、仿真与现实的复杂系统工程。不同类型的机器人(工业机械臂、服务机器人、人形机器人等)训练方法差异很大,但核心逻辑是相通的。 下面将梳理机器人训练的核心流程、关键技术和不同范式: 一、 机器人训练的总体流程 一个完整的机器人训练周期通常包含以下闭环: 感知 → 决策 → 执行 → 反馈 → 学习与优化 二、 核心训练方法与技术 机器人训练主要分为两大类:传统方法和基于机器学习(尤其是强化学习)的方法。 1. 传统方法(基于模型与规则) * 原理:工程师为机器人建立精确的数学模型(运动学、动力学模型),并编写明确的控制规则和任务逻辑。 * 如何训练: * 系统辨识:通过让机器人执行特定动作并收集数据,来反推和校准其数学模型参数。 * 轨迹规划:在已知模型的基础上,规划出最优、无碰撞的运动路径。 * PID控制:调试比例、积分、微分参数,让机器人动作稳定精准。 * 适用场景:结构化环境中的重复性任务,如汽车制造线上的焊接、喷涂。 2.