• SpringSecurity + jwt + vue2 实现权限管理 , 前端Cookie.set() 设置jwt token无效问题(已解决)


    问题描述

    今天也是日常写程序的一天 , 还是那个熟悉的IDEA , 还是那个熟悉的Chrome浏览器 , 还是那个熟悉的网站 , 当我准备登录系统进行登录的时候 , 发现会直接重定向到登录页 , 后端也没有报错 , 前端也没有报错 , 于是我得脸上又多了一张痛苦面具 , 紧接着在前端疯狂debug…寻找问题 , 我前端登录的部分逻辑是这样的 :

    1.登录成功之后 , 后端会响应一个jwt token , 这个jwt token的载荷有角色、权限、用户等信息
    2.然后我会判断响应状态码 , 如果是200的话 , 就使用 Cookies.set(TokenKey, token , {expires : val}) 将jwt token存到cookie中 ,如果不是200的话 ,弹出错误消息提示
    3.登录成功之后 ,会有一个js文件判断是否可以从cookie获取道token ,如果可以获取到 ,正常路由 , 然后跳转页面 , 如果获取不到的话 , 然后进行重定向到登录页面

    这就导致我非常的奇怪 ,后端接口也没有问题 ,jwt token也响应到前端了,并且前端debug的时候也可以拿到 ,但就是**Cookies.set(TokenKey, token , {expires : val})**代码执行完毕之后 ,我f12看了一下cookie ,尽然没有存进去? 瞬间懵逼 , 因为昨天还是好好的 ,唯一就动了菜单表的数据 ,然后我又恢复了一下菜单表 , 发现又可以了 , 紧接着又是一系列的数据比对操作 …以为是数据的问题
    在这里插入图片描述

    然后还没有找到问题 , 于是我就换了一下思路 , 对比了一下可以登录和不可以登录的两个jwt token , 发现长度不一样 ,于是手动在浏览器添加了一下jwt token,发现报错,这个时候问题也就出来了,由于jwt token中的载荷包含了角色、权限、用户等信息,角色和用户的数据都很小 ,只剩下权限了,而我的权限是再菜单表中的,昨天又只动了菜单表的数据 :

    所以问题就是 , jwt生成token的长度是和载荷有关系的,由于昨天加了菜单表的数据 ,导致了jwt载荷比较大 , 从而生成的jwt token 也比较大 ,所以再使用Cookies.set(TokenKey, token , {expires : val}) 将token放入cookie时无效

    知道问题之后我的痛苦面具也就没了 ,解决问题就好说了 , 下面是解决办法 :

    解决办法

    使用压缩算法将jwt的载荷数据进行压缩 ,解析jwt token的时候先将载荷进行解压缩:

    代码

    	/**
    	 * 将数据进行压缩
    	 * @param data 数据
    	 * @return 压缩之后的结果
    	 */
    	private String compress(String data) {
    		try {
    			byte[] input = data.getBytes("UTF-8");
    			byte[] output = new byte[input.length];
    			Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
    			deflater.setInput(input);
    			deflater.finish();
    			int compressedDataLength = deflater.deflate(output);
    			byte[] result = new byte[compressedDataLength];
    			System.arraycopy(output, 0, result, 0, compressedDataLength);
    			return new String(result, "ISO-8859-1");
    		} catch (Exception e) {
    			throw new RuntimeException("Failed to compress data", e);
    		}
    	}
    
    	/**
    	 * 将压缩之后的数据进行解压
    	 * @param compressedData 需要解压的数据
    	 * @return 解压之后的数据
    	 */
    	private String decompress(String compressedData) {
    		try {
    			byte[] input = compressedData.getBytes("ISO-8859-1");
    			byte[] output = new byte[input.length * 2];
    			Inflater inflater = new Inflater();
    			inflater.setInput(input);
    			int decompressedDataLength = inflater.inflate(output);
    			byte[] result = new byte[decompressedDataLength];
    			System.arraycopy(output, 0, result, 0, decompressedDataLength);
    			return new String(result, "UTF-8");
    		} catch (Exception e) {
    			throw new RuntimeException("Failed to decompress data", e);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    使用

    这里是创建jwt token的代码 ,解析jwt token的代码也是类似

    	/**
    	 * 创建JWT
    	 *
    	 * @param rememberMe  记住我
    	 * @param id          用户id
    	 * @param subject     用户名
    	 * @param roles       用户角色
    	 * @param authorities 用户权限
    	 * @return {@link String }
    	 */
    	public String createJWT(Boolean rememberMe, String id, String subject, List<String> roles, Collection<? extends GrantedAuthority> authorities) {
    		Date now = new Date();
    		Gson gson = new Gson();
    		//生成JWT的时间
    		long nowMillis = System.currentTimeMillis();
    		// 生成加密key
    		SecretKey key = generalKey();
    
    		String compress = compress(gson.toJson(authorities));
    
    		// 为payload添加各种标准声明和私有声明了
    		JwtBuilder builder = Jwts.builder()
    				// 设置jti(JWT ID):是JWT的唯一标识,从而回避重放攻击。
    				.setId(id)
    				// sub代表这个JWT的主体,即它的所有人。
    				.setSubject(subject)
    				// jwt签收者
    				.setIssuedAt(now)
    				// 设置签名使用的签名算法和签名使用的秘钥
    				.signWith(SignatureAlgorithm.HS256, key)
    				// 创建Payload
    				.claim("roles", roles)
    				.claim("authorities", compress);
    
    		// 设置过期时间
    		long ttlMillis = rememberMe ? Constants.JWT_REMEMBER : Constants.JWT_TTL;
    		if (ttlMillis > 0) {
    			long expMillis = nowMillis + ttlMillis;
    			Date exp = new Date(expMillis);
    			builder.setExpiration(exp);
    		}
    
    		String jwt = builder.compact();
    		// 将生成的JWT保存至Redis
    		stringRedisTemplate.opsForValue()
    				.set(Constants.REDIS_JWT_KEY_PREFIX + subject, jwt, ttlMillis, TimeUnit.MILLISECONDS);
    		return jwt;
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    到此,问题就解决啦 , 可能编程就是这样 ,编程的过程中会时而遇到困难和挫折,这是相当正常的。同时它是一个充满挑战和解决问题的过程,但也同样带来了许多乐趣和成就感。

  • 相关阅读:
    Python可视化招聘信息聚合系统 (附源码)!
    Mysql详细安装步骤
    python逆向基础流程(纯小白教程)
    java计算机毕业设计基于安卓Android/微信小程序的英语单词学习APP系统
    IPV4地址、VLSM以及CIDR
    【无标题】
    web常见的攻击方式有哪些?如何防御?
    【STL】list的模拟实现
    汪汪熊の模板
    鸡兔同笼问题python程序怎么写
  • 原文地址:https://blog.csdn.net/qq_45001002/article/details/133787611