You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot项目中JWT Token可被解码的原因及安全优化疑问

关于Spring Boot中JWT Token的安全性疑问

我在Spring Boot项目中使用JWT Token,发现该Token可通过官方网站解码并还原所有数据,我的Token为:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJTdGVlbERvYyIsImV4cCI6MTU1NzU2ODQ1MiwibG9naW5JZCI6ImFkbWluIiwidXNlcklkIjoyLCJlbWFpbElkIjoiZGhpcmVuZHJhQHRlc3QuY29tIn0.o4UKtwO22cAXLjIsUuDRgAQf7OjUbe_O9DbSGxPYgfQ

以下是我的实现代码:

public class JwtHelper { 
    static String subject = "SteelDoc"; 
    static String secret = "faa76006-6fef-413d-9cae-a05e63170cbf"; 
    Integer userId = null; 
    String loginId = ""; 
    String emailId = ""; 

    public String getUserName() { 
        return loginId; 
    } 

    public void setUserName(String userName) { 
        this.loginId = userName; 
    } 

    public Integer getUserId() { 
        return userId; 
    } 

    public void setUserId(Integer userId) { 
        this.userId = userId; 
    } 

    public String getToken(String loginId, Integer userId, String email) { 
        try { 
            Date d = new Date(); 
            Date de = d; 
            de.setYear(de.getYear() + 1); 
            String jwt = Jwts.builder()
                .setSubject(subject)
                .setExpiration(de)
                .claim("loginId", loginId)
                .claim("userId", userId)
                .claim("emailId", email)
                .signWith(SignatureAlgorithm.HS256, secret.getBytes("UTF-8"))
                .compact(); 
            return jwt; 
        } catch (UnsupportedEncodingException e) { 
            e.printStackTrace(); 
            return null; 
        } 
    } 

    public boolean validateToken(String token) { 
        String jwt = token; 
        Jws<Claims> claims; 
        try { 
            claims = Jwts.parser()
                .setSigningKey(secret.getBytes("UTF-8"))
                .parseClaimsJws(jwt); 
            userId = (Integer) claims.getBody().get("userId"); 
            loginId = (String) claims.getBody().get("loginId"); 
            emailId = (String) claims.getBody().get("emailId"); 
            return true; 
        } catch (ExpiredJwtException e) { 
            e.printStackTrace(); 
        } catch (UnsupportedJwtException e) { 
            e.printStackTrace(); 
        } catch (MalformedJwtException e) { 
            e.printStackTrace(); 
        } catch (SignatureException e) { 
            e.printStackTrace(); 
        } catch (IllegalArgumentException e) { 
            e.printStackTrace(); 
        } catch (UnsupportedEncodingException e) { 
            e.printStackTrace(); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
        return false; 
    }
}

我有三个技术疑问:

  1. 为何JWT Token会被轻易解码并还原数据?
  2. 若JWT存在此安全特性,为何仍被广泛使用?
  3. 有没有方法增强JWT Token的安全性?

解答

1. 为何JWT Token会被轻易解码并还原数据?

这其实是JWT的设计特性,而非漏洞。JWT由Header(头部)、Payload(载荷)、Signature(签名)三部分组成:

  • Header和Payload都是用Base64URL编码的,这种编码只是把JSON格式转成适合HTTP传输的字符串,完全可逆、不是加密,所以任何人都能解码出里面的内容。
  • JWT的安全核心在Signature部分:服务器用密钥对Header和Payload进行签名,后续验证时会重新计算签名并对比,只要密钥没泄露,篡改过的Token会因为签名不匹配被拒绝。

简单说,JWT不负责隐藏数据,只负责保证数据没被篡改。

2. 若JWT存在此安全特性,为何仍被广泛使用?

这种“可解码”的设计恰恰是JWT的优势,它能成为主流身份认证方案的原因包括:

  • 无状态认证:服务器不用存储会话,靠密钥验证签名就能确认Token有效性,完美适配分布式、微服务架构,减少存储和同步成本。
  • 跨平台兼容:Base64URL和JSON是通用格式,几乎所有编程语言都能轻松解析,方便在不同服务间传递身份信息。
  • 性能优化:Payload可以携带常用的非敏感身份信息(比如用户ID、登录名),避免每次请求都查数据库,提升接口响应速度。
  • 标准成熟:遵循RFC 7519规范,有大量第三方库支持,集成成本极低。

当然,这要求我们绝对不能在Payload里放敏感数据(比如密码、银行卡号),否则会被轻易泄露。

3. 有没有方法增强JWT Token的安全性?

结合你的代码,给你几个实用的优化方向:

  • 剔除Payload中的敏感数据:只保留必要的非敏感标识(比如用户ID、登录名),这是最基础的安全原则。
  • 更换签名算法:你当前用的HS256是对称加密,密钥必须严格保密,一旦泄露就会被伪造Token。分布式场景建议用RS256非对称算法——私钥在服务器端签名,公钥对外公开验证,即使公钥泄露也无法伪造Token。
  • 缩短过期时间:你代码里设置1年过期太长了!建议改成15分钟到1小时,同时配合Refresh Token机制:用有效期更长的Refresh Token来获取新的访问Token,既保证安全,又不用频繁登录。
  • HTTPS传输:一定要通过HTTPS传递JWT,避免Token在网络传输中被窃听。
  • 密钥安全管理:不要像现在这样把密钥硬编码到代码里,应该用环境变量、配置中心或密钥管理服务存储,防止密钥泄露。
  • 补充标准字段验证:在Payload中加入iss(签发者)、aud(受众)字段,验证Token的来源和使用范围;加入jti(唯一标识),实现Token黑名单,在用户注销、改密码时失效指定Token。
  • 修复过期时间代码:你当前的Date de = d; de.setYear(de.getYear() + 1);是错误的(setYear已过时,还会修改原Date对象),建议用Java 8+的java.time包:
Instant now = Instant.now();
Instant expiration = now.plus(Duration.ofHours(1)); // 设置1小时过期

内容的提问来源于stack exchange,提问作者Dhirendra Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:17:22