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

如何实现共享密钥版NimbusJwtDecoder与NimbusJwtEncoder

问题描述

我尝试使用 org.springframework.security.oauth2.jwt 包提供的编码器/解码器,实现基于共享密钥的JWT处理方案,但在编码令牌时抛出 JwtEncodingException 异常,导致实现失败。此前曾以其他形式提出过该问题,本次提供可直接运行的简易示例用于复现问题:

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.source.ImmutableSecret;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.security.oauth2.jwt.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class EncoderDecoderTest {
  public static String secret = "j8IoV1jF67";

  public JwtEncoder jwtEncoder() throws JOSEException {
    SecretKey originalKey = new SecretKeySpec(secret.getBytes(), "AES");
    JWKSource<SecurityContext> immutableSecret = new ImmutableSecret<SecurityContext>(originalKey);
    return new NimbusJwtEncoder(immutableSecret);
  }
  public JwtDecoder jwtDecoder() {
    SecretKey originalKey = new SecretKeySpec(secret.getBytes(), "AES");
    NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(originalKey).build();
    return jwtDecoder;
  }

  public void tester() throws JOSEException {
    JwtClaimsSet claims = JwtClaimsSet.builder()
            .issuer("self")  //Only this for simplicity
            .build();
    var encoder = jwtEncoder();
    //This line throws the exception
    String token = encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
    System.out.println(token);

    var decoder = jwtDecoder();
    Jwt jwt = decoder.decode(token);
    System.out.println(jwt.getIssuer());
  }

  public static void main(String[] args) throws JOSEException {
    new EncoderDecoderTest().tester();
  }
}

经排查根因为AES不属于合法的JWT签名算法,现需要实现基于共享密钥、使用HMAC-SHA256算法的NimbusJwtDecoder与NimbusJwtEncoder配置,多次尝试未成功,需要可行的修复方案。


问题根因

代码存在三个核心问题:

  1. 密钥算法类型错误:AES是对称加密算法,不属于JWT支持的签名算法。HMAC系列JWT签名要求密钥算法使用HmacSHA256(对应HS256)、HmacSHA384(对应HS384)、HmacSHA512(对应HS512)。
  2. 密钥长度不达标:HS256算法要求密钥长度至少为256位(即32字节),示例中使用的密钥仅10字节,不满足算法安全要求,会触发校验异常。
  3. 编码时未指定签名算法:NimbusJwtEncoder无法自动推断签名算法,需要在编码参数中显式指定JWS头部的算法标识,否则无法完成签名逻辑。

修复方案

1. 准备符合要求的密钥

HS256要求密钥长度≥32字节,HS384≥48字节,HS512≥64字节,生产环境请从安全配置源(环境变量、配置中心)读取密钥,禁止硬编码。示例使用32字节测试密钥:

// 测试用32字节密钥,满足HS256最低长度要求
public static final String SECRET = "j8IoV1jF67abcdefghijklmnopqrstuvw";

2. 修正编码器配置

将密钥算法替换为HmacSHA256,密钥字符集指定为UTF-8避免编码不一致问题:

public JwtEncoder jwtEncoder() {
    SecretKey secretKey = new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
    JWKSource<SecurityContext> jwkSource = new ImmutableSecret<>(secretKey);
    return new NimbusJwtEncoder(jwkSource);
}

3. 修正解码器配置

显式指定解码器使用的MAC算法为HS256,避免算法混淆攻击:

public JwtDecoder jwtDecoder() {
    SecretKey secretKey = new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
    return NimbusJwtDecoder.withSecretKey(secretKey)
            .macAlgorithm(MacAlgorithm.HS256)
            .build();
}

4. 修正令牌生成逻辑

编码令牌时传入指定HS256算法的JWS头部:

public void tester() {
    JwtClaimsSet claims = JwtClaimsSet.builder()
            .issuer("self")
            .build();
    // 显式指定JWS签名算法为HS256
    JwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256).build();
    JwtEncoder encoder = jwtEncoder();
    String token = encoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).getTokenValue();
    System.out.println("生成的Token:" + token);

    JwtDecoder decoder = jwtDecoder();
    Jwt jwt = decoder.decode(token);
    System.out.println("解析得到的签发方:" + jwt.getIssuer());
}

完整可运行代码

需要额外引入java.nio.charset.StandardCharsets依赖:

import com.nimbusds.jose.jwk.source.ImmutableSecret;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.security.oauth2.jwt.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class EncoderDecoderTest {
    public static final String SECRET = "j8IoV1jF67abcdefghijklmnopqrstuvw";

    public JwtEncoder jwtEncoder() {
        SecretKey secretKey = new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        JWKSource<SecurityContext> jwkSource = new ImmutableSecret<>(secretKey);
        return new NimbusJwtEncoder(jwkSource);
    }

    public JwtDecoder jwtDecoder() {
        SecretKey secretKey = new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        return NimbusJwtDecoder.withSecretKey(secretKey)
                .macAlgorithm(MacAlgorithm.HS256)
                .build();
    }

    public void tester() {
        JwtClaimsSet claims = JwtClaimsSet.builder()
                .issuer("self")
                .build();
        JwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256).build();
        JwtEncoder encoder = jwtEncoder();
        String token = encoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).getTokenValue();
        System.out.println("生成的Token:" + token);

        JwtDecoder decoder = jwtDecoder();
        Jwt jwt = decoder.decode(token);
        System.out.println("解析得到的签发方:" + jwt.getIssuer());
    }

    public static void main(String[] args) {
        new EncoderDecoderTest().tester();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 07:45:32