【问题标题】:How to get values using @Value annotation before Bean Creation using @Component annotation in Java Spring Bootjava - 如何在Java Spring Boot中使用@Component注释在创建Bean之前使用@Value注释获取值
【发布时间】:2022-02-24 15:41:03
【问题描述】:

我目前正在尝试学习使用 Java Spring Boot Framework 进行后端开发。对于 POC,我正在构建安全令牌服务 (STS) 服务器,其作用是为有效的用户身份验证请求生成 JWT 令牌。

我遇到了一个问题,我尝试使用 @Value 注释从 application.yml 文件中获取 JWT Secret Key。由于代码中的硬编码秘密被认为是不好的做法。所以我想使用@Value 注释从 application.yml 文件中注入这个秘密,然后使用这个秘密密钥对令牌进行签名,但我在这里得到 NULL VALUE EXCEPTION。下面是我正在尝试做的代码sn-p

@Slf4j
@Data
@Component
public class JwtUtil {

    @Value("${JWT.secret: JWT_SECRET_KEY}")
    public String jwtSecret;
    private Integer expiryTimeInMinutes = 100000;
    private Algorithm tokenSigningAlgo = Algorithm.HMAC256(jwtSecret);

    public String generateJWT(String subject, Map<String, Object> claims) {
        return JWT
                .create()
                .withSubject(subject)
                .withPayload(claims)
                .withExpiresAt(new Date(System.currentTimeMillis() + expiryTimeInMinutes * 60 * 1000))
                .sign(tokenSigningAlgo);
    }
}

这是我得到的例外

Constructor threw exception; nested exception is java.lang.IllegalArgumentException: The Secret cannot be null

我已将此 JWT.secret 值存储在 application.yml 中。

JWT:
  secret: "SECRET_KEY"
  expiryTimeInMinutes: 20

从我的调试中我了解到@Value 注解在创建类 JwtUtil 的 bean 之后初始化 secret 的值。因为我在删除 JWT 创建逻辑(抛出空值异常)后尝试记录 secret 的值,然后它工作正常。我从 application.yml 中获得了预期值。

有什么方法可以在创建 JwtUtil bean 之前获取 @Value 注释值?或者你们能建议我实现这一目标的最佳实践吗?

提前致谢!

【问题讨论】:

  • 创建一个bean发生在调用目标类的构造函数之后。调用了类 JwtUtil 的构造函数,然后调用了这部分私有算法 tokenSigningAlgo = Algorithm.HMAC256(jwtSecret); JwtUtil 的 Bean 尚未创建。其中一种方法替换 Algorithm.HMAC256(jwtSecret);使用 PostCostruct 注释初始化方法

标签: java spring spring-boot spring-mvc jwt


【解决方案1】:

在调用目标类的构造函数之后创建一个 bean。 类JwtUtil的构造函数被调用,然后被调用这部分private Algorithm tokenSigningAlgo = Algorithm.HMAC256(jwtSecret); JwtUtil 的 Bean 尚未创建。 其中一种方法是用PostCostruct注解将Algorithm.HMAC256(jwtSecret)替换为init方法

【讨论】:

    【解决方案2】:

    试试@ConfigurationProperties

    @Component
    @Getter
    @Setter
    @ConfigurationProperties(prefix = "oauth2", ignoreUnknownFields = false)
    public class OauthProperties {
        private String jwtSecret;
        private Long jwtExpirationInMs;
    }
    

    application.yml

    oauth2:
      jwtSecret: SecretKey
      jwtExpirationInMs: 86400000
    

    在此处注入您的配置类

    @Component
    @AllArgsConstructor
    public class JwtTokenProvider {
    
        private final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
    
        private OauthProperties oauthProperties;
    ...
    

    【讨论】:

      【解决方案3】:

      您所面临的问题可以通过添加构造函数来解决,例如

      @Slf4j
      @Data
      @Component
      public class JwtUtil {
          private static final Integer expiryTimeInMinutes = 100000;
          private Algorithm tokenSigningAlgo;
      
          JwtUtil(@Value("${JWT.secret: JWT_SECRET_KEY}") String jwtSecret) {
               tokenSigningAlgo = Algorithm.HMAC256(jwtSecret);
          };
      
          public String generateJWT(String subject, Map<String, Object> claims){
              return JWT
                      .create()
                      .withSubject(subject)
                      .withPayload(claims)
                      .withExpiresAt(new Date(System.currentTimeMillis() + expiryTimeInMinutes * 60 * 1000))
                      .sign(tokenSigningAlgo);
          }
      }
      

      这会稍微改变实例化流程,因为它将确保在执行 Algorithm.HMAC256(jwtSecret) 方法之前已为 jwtSecret 构造函数参数分配了正确的值。

      【讨论】:

      • 这解决了这个问题。非常感谢!
      【解决方案4】:

      一个好的做法是在构造函数中注入String jwtSecret 以便于测试,例如:

      
      public JwtUtil(@Value("${JWT.secret}") String jwtSecret) {
          this.tokenSigningAlgo = Algorithm.HMAC256(jwtSecret);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-01
        • 2014-06-30
        • 1970-01-01
        • 2013-03-25
        • 1970-01-01
        相关资源
        最近更新 更多