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认证流程
- 前端拿到用户名和密码,发送给后端;
- 后端进行判断,如果登录成功,同时生成Jwt返回给前端;
- 前端收到Jwt后保存到localstorage/cookie中;
- 以后每次请求都携带Jwt;
- 服务端对Jwt进行验证,如果验证成功,放心;
- 否则非法/过期;
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,当某一次访问业务接口时:
- 先被拦截器拦截;
- 如果鉴权token没有过期,那么直接放行;
- 验证到鉴权token过期,这时候如果刷新token没有过期,那么我们先去访问/refresh接口,重新发布一个鉴权token,之后就可以拿着这个鉴权token去访问业务接口;
- 如果鉴权token过期,同时刷新token过期,那么用户就必须重新登录了。

浙公网安备 33010602011771号