单应用时期,通常使用 Cookies 和 Session 进行会话管理。
用户登录后,服务器创建一个唯一的会话标识符(Session ID),将其存储在浏览器的 Cookies
中,并在服务端维护一个关联该标识符的会话对象。
这种方式的问题在于,会话状态存储在服务器本地内存中,如果有多个应用实例,会导致会话状态
无法共享。
为了解决单应用的局限性,引入 Redis 作为分布式缓存存储会话数据。
Redis 具有高性能、可伸缩性,而且可以集中管理分布式环境下的会话数据
简单点讲,就是多个应用连接同一个Redis实现会话共享
、
像SpringSession其实用的也是这套
其实原先的Token也是仿造了Session的机制,原理就是后端返回一个Token,然后每次请求是都将Token带到服务器,用Token作为Key来找对应的Value
JWT 是一种基于 Token 的认证机制,通过在客户端存储 Token,而不需要在服务器端保留会话状态。
可以说JWT是专门进行分布式会话管理的
在分布式系统中,不同服务之间可能需要共享用户会话信息。JWT 的结构允许信息被安全地编码为一个 Token,并在服务之间传递,而无需依赖共享的存储机制。
使用JWT
依赖
<!--JWT的三个依赖--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency>
//创建JWT @Test public void testCreatedJwt() { // String s= UUID.randomUUID().toString().replace("-", ""); // System.out.println("s="+s); //9f4224f2fb0d44cbbe5d03abd14a3bad String key = "9f4224f2fb0d44cbbe5d03abd14a3bad";//每个应用都需要用来解密的一个key 我这里用UUID生成 //创建SeceretKey SecretKey secretKey = Keys.hmacShaKeyFor(key.getBytes(StandardCharsets.UTF_8)); Map<String, Object> data = new HashMap<>(); data.put("uid", 1001); data.put("name", "小王王"); data.put("role", "经理"); //DateUtils.addMinutes(new Date(), 10);用commons-lang3的工具类 指定时间上加多少分钟也可以 //创建JWT 使用Jwts类 String jwtToken = Jwts.builder().signWith(secretKey, SignatureAlgorithm.HS256) 设置签名密钥和使用的算法 .setExpiration(new Date(new Date().getTime() + 60 * 10 * 1000))//设置过期时间10分钟之后 .setIssuedAt(new Date())//设置令牌签发时间,即当前时间 .setId(UUID.randomUUID().toString())// 设置JWT的唯一标识符(JTI) .addClaims(data)// 添加额外的声明(payload 中的数据) .compact();// 构建并压缩JWT成为一个字符串 //eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDE0NjUyOTUsImlhdCI6MTcwMTQ2NDY5NSwianRpIjoiZDVmY2FlZmItYzJkMi00YzMxLWFhYzktYjcwNjZiYTUyNzViIiwicm9sZSI6Iue7j-eQhiIsIm5hbWUiOiLlsI_njovnjosiLCJpZCI6MTAwMX0.VLcmm-8dOjKVeobSwysXPZHvDkul-n3wLKAzSM7P4lw System.out.println(jwtToken); } //读JWT @Test public void readJwtTest() { String jwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDE0NzQwNjYsImlhdCI6MTcwMTQ3MzQ2NiwianRpIjoiMWU4OWM4YzktNGI2Yy00NmVmLWI1YWMtZDkyODEyODUxYTZmIiwidWlkIjoxMDAxLCJyb2xlIjoi57uP55CGIiwibmFtZSI6IuWwj-eOi-eOiyJ9.W1IfvHWHc-vbwM8tSwXUUFjFgZrr47qy8aC9rCedvUw"; String key = "9f4224f2fb0d44cbbe5d03abd14a3bad";//每个应用都需要用来解密的一个key //创建SeceretKey SecretKey secretKey = Keys.hmacShaKeyFor(key.getBytes(StandardCharsets.UTF_8)); //解析JWT 没有异常 继续读 有异常说明这个jwtToken是无效的 ExpiredJwtException过期异常 Jws<Claims> claimsJws = Jwts.parserBuilder()//创建一个JWT解析器的构建器对象,该对象用于配置JWT解析器的各种参数。 .setSigningKey(secretKey)//设置JWT解析器使用的签名密钥,以便验证JWT的真实性 .build() .parseClaimsJws(jwtToken);//使用构建好的JWT解析器解析JWT令牌。这个方法返回一个Jws<Claims>对象,其中包含了JWT的各种声明(claims),以及相关的信息,如签名等。 //读数据 Claims body = claimsJws.getBody(); //通过body可以获取所有原先放进去的值 举例 Integer id = body.get("uid", Integer.class); System.out.println("uid=" + id); Object uid = body.get("uid"); System.out.println("uid=" + uid); String name = (String) body.get("name"); System.out.println("name=" + name); //JWT的唯一标识符 String id3 = body.getId(); System.out.println("唯一表示符:" + id3); //获取设置的过期时间 Date expiration = body.getExpiration(); System.out.println("过期时间:" + expiration); }