如何保护@ConfigurationProperties 类免受更改?

Posted

技术标签:

【中文标题】如何保护@ConfigurationProperties 类免受更改?【英文标题】:How to protect @ConfigurationProperties classes from changes? 【发布时间】:2017-11-27 16:50:43 【问题描述】:

要使用 @ConfigurationProperties 注释,必须创建一个具有类似 getter 和 setter 的类:

@ConfigurationProperties(prefix = "some")
public class PropertiesConfig 
    private boolean debug;

    public boolean isDebug() 
        return debug;
    

    public void setDebug(boolean debug) 
        this.debug = debug;
    

但这会导致有人试图通过调用来修改此值:

@Autowire
private PropertiesConfig config;        
//....

config.setDebug(true);

有没有办法在没有设置器和外部解析器/读取器类的情况下创建 @ConfigurationProperties 注释类?

【问题讨论】:

【参考方案1】:

使用尽可能少的样板代码的一种方法是使用仅带有 getter 的接口

public interface AppProps 
    String getNeededProperty();

并借助 Lombok 的 @Getter@Setter 注释在实现中摆脱样板的 getter 和 setter:

@ConfigurationProperties(prefix = "props")
@Getter
@Setter
public class AppPropsImpl implements AppProps 
    private String neededProperty;

然后,对于只能通过接口访问其他 bean 的 bean bo,可以考虑将其放入配置中,而不是将其标记为 @Component 或在主应用程序类上使用 @EnableConfigurationProperties(AppPropsImpl.class)按界面:

@Configuration
@EnableConfigurationProperties
public class PropsConfiguration  
    @Bean
    public AppProps appProps()
        return new AppPropsImpl();
    

现在这个 bean 只能通过接口注入,这使得其他 bean 无法使用 setter:

public class ApplicationLogicBean 
    @Autowired
    AppProps props;

    public void method()
        log.info("Got " + props.getNeededProperty());
    

使用 Spring Boot 1.5.3 和 Lombok 1.16.16 测试。

【讨论】:

【参考方案2】:

这样的东西很好用

@Configuration
class MyAppPropertiesConfiguration 

    @Bean
    @ConfigurationProperties(prefix = "some")
    public PropertiesConfig propertiesConfig () 
        return new PropertiesConfigImpl();
    

    public interface PropertiesConfig 
            public boolean isDebug();
    

    private static class PropertiesConfigImpl implements PropertiesConfig 
        private boolean debug;

        @Override
        public boolean isDebug() 
            return debug;
        

        public void setDebug(boolean debug) 
            this.debug = debug;
        
    

然后

@Autowired PropertiesConfig properties;

【讨论】:

我不得不将接口移动到它自己的文件中。然后效果很好。【参考方案3】:

从 Spring Boot 2.2 开始,终于可以定义用@ConfigurationProperties 修饰的不可变类了。非常感谢 Spring 开发人员不断改进他们的框架。The documentation 显示了一个示例。 您只需要声明一个带有要绑定的字段的构造函数(而不是 setter 方式):

@ConstructorBinding
@ConfigurationProperties(prefix = "some")
public class PropertiesConfig 
    private boolean debug;

    public AcmeProperties(boolean enabled) 
        this.enabled = enabled;   
    

    public boolean isDebug() 
        return debug;
    


注意 1 :您必须定义一个且只有一个带有要绑定参数的构造函数:

在这个设置中,只有一个构造函数必须用 您希望绑定的属性列表,而不是其他属性 构造函数中的那些是绑定的。

注意 2:引入了 @DefaultValue 来定义不可变属性绑定的默认值。

可以使用@DefaultValue 指定默认值,同样的 将应用转换服务将字符串值强制转换为 缺失属性的目标类型。

这是从官方文档中获取的更详细的示例:

import java.net.InetAddress;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DefaultValue;
import org.springframework.boot.context.properties.ConstructorBinding;

@ConstructorBinding
@ConfigurationProperties("acme")
public class AcmeProperties 

    private final boolean enabled;

    private final InetAddress remoteAddress;

    private final Security security;

    public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) 
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    

    public boolean isEnabled()  ... 

    public InetAddress getRemoteAddress()  ... 

    public Security getSecurity()  ... 

    public static class Security 

        private final String username;

        private final String password;

        private final List<String> roles;

        public Security(String username, String password,
                @DefaultValue("USER") List<String> roles) 
            this.username = username;
            this.password = password;
            this.roles = roles;
        

        public String getUsername()  ... 

        public String getPassword()  ... 

        public List<String> getRoles()  ... 

    


【讨论】:

【参考方案4】:

不可能开箱即用。 @ConfigurationProperties bean 必须有标准的 getter 和 setter。您可能需要考虑此答案中描述的方法:Immutable @ConfigurationProperties

或者是这样的:

@Component
public class ApplicationProperties 

  private final String property1;
  private final String property2;

  public ApplicationProperties(
    @Value("$some.property1") String property1,
    @Value("$some.other.property2) String property2) 
    this.property1 = property1;
    this.property2 = property1;
  

  //
  // ... getters only ...
  //


【讨论】:

不幸的是,有几个极端情况不适用于这种方法。例如,使用@Valueapplication.yaml 中的对象作为Map&lt;String, Object&gt; 注入将无法正常工作,而@ConfigurationProperties 方法则可以正常工作。

以上是关于如何保护@ConfigurationProperties 类免受更改?的主要内容,如果未能解决你的问题,请参考以下文章

如何去除excel的宏保护?

web内容如何保护:如何有效地保护 HTML5 格式的视频内容?

excel如何撤销工作表保护

【转】如何保护Java代码

如何关闭数据执行保护.

Php代码如何实现代码保护,不被窃取?