网站首页 > 技术文章 正文
什么是 Token
Token(令牌)是一种用于身份验证和授权的机制,它是在用户登录后生成的一段字符串,用于识别用户的身份信息,以便在用户进行后续操作时进行身份验证和授权
Token是服务端生成的一串字符串,用来作为客户端的请求令牌。当用户首次登录成功后,服务端会生成一个token并返回给客户端,此后客户端只需要带上token字段,而无需再上传用户名和密码;但当token过期后,用户又会回到首次登录的状态,需要输入用户名和密码
什么是 JWT
Token的生成和解析通常采用JWT(JSON Web Token)协议,它是一种轻量级的身份验证和授权机制。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),其中头部和载荷都是使用Base64 编码的JSON 对象,签名是由头部、载荷和密钥生成的哈希值
JWT 的生成过程
- 创建 JWT 头部(Header)对象,包含算法和类型信息
- 创建 JWT 载荷(Payload)对象,包含用户信息和过期时间等信息
- 使用密钥对 JWT 头部和载荷进行签名生成 JWT 签名(Signature)
- 将 JWT 头部、载荷和签名拼接成一个字符串形式的 JWT
java
复制代码
Spring Boot 采用了标准的 JWT 头部格式, 如下: { "alg": "HS256", "typ": "JWT" } 因此默认情况下使用的是 SignatureAlgorithm.HS256 对称加密算法
但也支持其他的签名算法,比如:SignatureAlgorithm.RS256非对称加密算法
需要在 application.properties 或 application.yml 文件中通过对应的属性进行配置
---spring.security.jwt.signature-algorithm。例如:如果需要使用 RS256 算法,则可以设置该属性为 RS256
JWT 生成 token 的代码
java
复制代码
private static final String SECRET = "secret_key"; private static final long EXPIRATION_TIME = 3600000; public static String createJwtToken(String userId) { Map
配置文件里面配置通用变量
yml
复制代码
# SECRET 和 EXPIRATION_TIME 也可以在 application.yml 文件中进行配置,这样就可以通用了 spring: security: jwt: secret: secret_key expires: 86400000 # 过期时间,单位为毫秒,这里设置的过期时间为1天
使用配置文件里面配置的通用变量
java
复制代码
// 在代码中获取配置文件的值 @Value("${
spring.security.jwt.secret}") private static String SECRET; @Value("${
spring.security.jwt.expires}") private static long EXPIRATION_TIME;
方法解析
builder
builder: 用于创建 JWT 字符串的静态方法,它是由 Jwts 类提供的。该方法返回一个 JwtBuilder 对象,用于构建JWT并生成 JWT 字符串
setClaims
setClaims: 用于设置载荷部分的方法。在 JWT 中,载荷是一个JSON 对象,它包含了一些与用户身份验证和授权相关的信息,例如用户ID、用户名、角色、权限等
java
复制代码
// 用法一 claims.put("userId", userId); return Jwts.builder() .setClaims(claims) .compact(); // 用法二 return Jwts.builder() .claims("userId", userId) .claims("username", username) .compact();
setIssuedAt
setIssuedAt: 用于设置签发时间(issued at)的方法。在 JWT 中,签发时间是一个整数,表示 JWT 的生成时间,通常以秒为单位
setExpiration
setExpiration: 用于设置过期时间(expiration)的方法。在 JWT 中,过期时间也是一个整数,表示 JWT 的有效期,通常以秒为单位
注意:设置签发时间和过期时间的方法传入的时间都是 Date 对象,但 setIssuedAt、setExpiration 会将这个 Date 对象转换为 Unix 时间戳并编码到 JWT 中
生成时间的多种方法:
- System.currentTimeMillis(): 获取当前时间的毫秒数,类型为 long, 从而得到时间戳(单位为秒)
- new Date(): 返回当前时间的 Date 对象,包含当前时间的年、月、日、时、分、秒等信息 这个方法通常用于获取当前时间的具体信息或用于时间格式化
- getTime(): Date 对象的方法,用于获取当前时间的时间戳
- new Date(时间戳): 生成的是一个表示当前时间戳的 Date 对象
signWith
signWith: 用于设置签名的方法,可以使用不同的算法来对 JWT 进行签名。JWT 使用签名来保证 JWT 的真实性和完整性,防止 JWT 被篡改或伪造
txt
复制代码
signWith 方法接受两个参数,分别是签名算法和密钥 签名算法的类型必须要和 JWT 头部中的 “alg” 字段一致,否则验证时会提示签名无效 因此,在使用JWT进行签名时,必须要明确指定签名算法 签名算法是由 SignatureAlgorithm 枚举类提供的,它定义了一系列的签名算法,包括 HMAC 和 RSA 在 signWith 方法中: 如果为对称算法,则密钥应该是一个 byte[] 数组或 String 类型的字符串; 如果为非对称算法,则密钥应该是一个 Key 类型的对象,例如 RSAPrivateKey 或 RSAPublicKey 对称加密算法,需要使用相同的密钥进行加解密 非对称加密算法,使用公钥进行加密,私钥进行解密
txt
复制代码
SignatureAlgorithm.HS512 算法,属于 HMAC-SHA512 对称加密算法 SignatureAlgorithm.HS256 算法,属于 HMAC-SHA256 对称加密算法 SignatureAlgorithm.RS256 算法, 属于 RSA-SHA256 非对称加密算法
compact
compact: 用于将 JWT 编码为紧凑的字符串形式。即将 JWT 编码为没有空格、没有等号、没有加号、没有斜杠的紧凑字符串形式,以便在 URL 中传输
方法总览
方法 | 含义 |
Jwts.builder() | 创建一个 JwtBuilder 对象 |
JwtBuilder.setClaims() | 设置 JWT 的声明(claims) |
JwtBuilder.setIssuer() | 设置 JWT 的发行者(issuer) |
JwtBuilder.setSubject() | 设置 JWT 的主题(subject) |
JwtBuilder.setAudience() | 设置 JWT 的受众(audience) |
JwtBuilder.setIssuedAt() | 设置 JWT 的签发时间(issuedAt) |
JwtBuilder.setExpiration() | 设置 JWT 的过期时间(expiration) |
JwtBuilder.signWith() | 设置 JWT 的签名算法和密钥 |
JwtBuilder.compact() | 生成 JWT 字符串 |
JWT 的解析过程
- 从 JWT 字符串中解析出头部(Header)、载荷(Payload)和签名(Signature)
- 验证 JWT 签名是否正确,即:使用密钥对头部和载荷进行签名生成签名(Signature),并与解析出的签名进行比较,如果一致则验证通过
- 验证 JWT 是否过期,即:检查载荷中的过期时间是否在当前时间之前,如果过期则验证失败
后端获取 token 的方式通常有两种:
- 通过 HTTP 请求头获取:前端在请求时将 token 放置于 HTTP 请求头中,通常是以Authorization: Bearer
的形式,后端可以通过读取请求头中的Authorization字段获取 token - 通过请求参数获取:前端在请求时将 token 以某个参数名的形式传递,例如:token=
,后端可以通过解析请求参数获取 token
一般来说,第一种方式更为常用和安全,因为 HTTP 请求头中的内容不会被浏览器缓存或保存在历史记录中,而请求参数则可能会被保存。因此,建议在实现后端获取 token 的逻辑时,优先考虑使用 HTTP 请求头方式
arduino
复制代码
private static final String SECRET = "secret_key"; public static Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); }
方法解析
parser
parser: 用于解析 JWT 字符串的静态方法,它是由 Jwts 类提供的。该方法接受一个 JWT 字符串,并返回一个 JwtParser 对象,用于解析 JWT 字符串并验证其签名
setSigningKey
setSigningKey: 用于设置 JWT 签名密钥的方法,它是由 JwtBuilder 类提供的。该方法接受一个 Key 类型的参数,表示用于签名的密钥
txt
复制代码
在生成 JWT 时,需要将密钥传递给 signWith()方法以指定签名算法和密钥 在验证 JWT 时,也需要将密钥传递给 setSigningKey()方法以验证签名
txt
复制代码
签名密钥可以是对称密钥(例如HMAC密钥)或非对称密钥(例如RSA密钥对) 对称密钥适用于需要高性能和低成本的场景 非对称密钥适用于需要更高的安全性和可扩展性的场景
parseClaimsJws
parseClaimsJws: 是 JWT 解析方法,用于解析 JWT Token 并获取其中的载荷信息(payload),同时也会验证签名是否正确
getBody
getBody: 用于从 JWT 中获取载荷(payload)信息。调用此方法将返回一个 Claims 对象,其中包含了 JWT 载荷中的所有声明信息,可以通过 get 方法获取对应的声明值
方法总览
方法 | 含义 |
Jwts.parser() | 创建一个 JwtParser 对象 |
JwtParser.setSigningKey() | 设置 JWT 的签名密钥 |
JwtParser.parseClaimsJws() | 解析 JWT 字符串并返回一个 Claims 对象,包含了 JWT 中所有的声明(claims) |
使用 JWT 的优点
- 轻量级,传输效率高
- 可以保存用户信息,不需要在每次请求时重新获取用户信息
- 可以防止 CSRF 攻击,因为攻击者无法伪造有效的 JWT
- 可以跨域使用,因为 JWT 是通过签名验证的,而不需要依赖 cookie 等传统的跨域解决方案
使用 JWT 的缺点
- JWT 不能撤销,一旦签发了 JWT,就无法撤销,除非等到过期时间到期
- JWT 中包含用户信息,一旦 JWT 泄露,攻击者就可以获得用户的所有信息
- JWT 不能修改,一旦 JWT 签发,就无法修改其中的内容,除非等到过期时间到期
- 使用 JWT 需要密钥,密钥泄露后会导致 JWT 失去安全性
猜你喜欢
- 2025-03-18 用deepseek学前端《vue 前端接入后端接口,详细代码样例》
- 2025-03-18 Sa-Token,让你的权限认证更简单(sa用户)
- 2025-03-18 在 Spring Boot3 中整合 Redis 和 JWT 技术实现基于 Token 的授权认证机制
- 2025-03-18 基于 token 的多平台身份认证架构设计
- 2025-03-18 Deepseek 只需2步生成'流程图'方法:附详细步骤
- 2025-03-18 单点登录【SSO】(单点登录入口)
- 2025-03-18 OAuth2.0的安全解析(oauth2.0 client)
- 2025-03-18 cookie、session和token(cookie,session和token的区别)
- 2025-03-18 昨天的 Token 认证写错了,惊出一身汗,赶紧删除
- 2025-03-18 jwt与token+redis,哪种方案更好用
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端md5加密 (49)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- 前端接口 (46)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle约束 (46)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- mac oracle (47)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)