Spring BootSpringBoot 如何保证接口安全?老鸟们都是这么玩的!

Posted 逆流°只是风景-bjhxcc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring BootSpringBoot 如何保证接口安全?老鸟们都是这么玩的!相关的知识,希望对你有一定的参考价值。

文章目录

前言

如何保证接口安全?本文揭晓答案,请看下文。

为什么要保证接口安全

对于互联网来说,只要你系统的接口暴露在外网,就避免不了接口安全问题。 如果你的接口在外网裸奔,只要让黑客知道接口的地址和参数就可以调用,那简直就是灾难。

举个例子:你的网站用户注册的时候,需要填写手机号,发送手机验证码,如果这个发送验证码的接口没有经过特殊安全处理,那这个短信接口早就被人盗刷不知道浪费多少钱了。

那如何保证接口安全呢?

一般来说,暴露在外网的api接口需要做到防篡改和防重放才能称之为安全的接口。

防篡改

我们知道http 是一种无状态的协议,服务端并不知道客户端发送的请求是否合法,也并不知道请求中的参数是否正确。

举个例子, 现在有个充值的接口,调用后可以给用户增加对应的余额。

http://localhost/api/user/recharge?user_id=1001&amount=10

如果非法用户通过抓包获取到接口参数后,修改user_id 或 amount的值就可以实现给任意账户添加余额的目的。

如何解决

采用https协议可以将传输的明文进行加密,但是黑客仍然可以截获传输的数据包,进一步伪造请求进行重放攻击。如果黑客使用特殊手段让请求方设备使用了伪造的证书进行通信,那么https加密的内容也会被解密。
一般的做法有2种:

  1. 采用https方式把接口的数据进行加密传输,即便是被黑客破解,黑客也花费大量的时间和精力去破解。

  2. 接口后台对接口的请求参数进行验证,防止被黑客篡改;

  • 步骤1:客户端使用约定好的秘钥对传输的参数进行加密,得到签名值sign1,并且将签名值也放入请求的参数中,发送请求给服务端
  • 步骤2:服务端接收到客户端的请求,然后使用约定好的秘钥对请求的参数再次进行签名,得到签名值sign2。
  • 步骤3:服务端比对sign1和sign2的值,如果不一致,就认定为被篡改,非法请求。

防重放

防重放也叫防复用。简单来说就是我获取到这个请求的信息之后什么也不改,,直接拿着接口的参数去 重复请求这个充值的接口。此时我的请求是合法的, 因为所有参数都是跟合法请求一模一样的。重放攻击会造成两种后果:

  1. 针对插入数据库接口:重放攻击,会出现大量重复数据,甚至垃圾数据会把数据库撑爆。
  2. 针对查询的接口:黑客一般是重点攻击慢查询接口,例如一个慢查询接口1s,只要黑客发起重放攻击,就必然造成系统被拖垮,数据库查询被阻塞死。

对于重放攻击一般有两种做法:

基于timestamp的方案

每次HTTP请求,都需要加上timestamp参数,然后把timestamp和其他参数一起进行数字签名。因为一次正常的HTTP请求,从发出到达服务器一般都不会超过60s,所以服务器收到HTTP请求之后,首先判断时间戳参数与当前时间比较,是否超过了60s,如果超过了则认为是非法请求。

一般情况下,黑客从抓包重放请求耗时远远超过了60s,所以此时请求中的timestamp参数已经失效了。 如果黑客修改timestamp参数为当前的时间戳,则sign1参数对应的数字签名就会失效,因为黑客不知道签名秘钥,没有办法生成新的数字签名。

但是这种方式的漏洞也是显而易见,如果在60s之内进行重放攻击,那就没办法了,所以这种方式不能保证请求仅一次有效。

老鸟们一般会采取下面这种方案,既可以解决接口重放问题,又可以解决一次请求有效的问题。

基于nonce + timestamp 的方案

nonce的意思是仅一次有效的随机字符串,要求每次请求时该参数要保证不同。实际使用用户信息+时间戳+随机数等信息做个哈希之后,作为nonce参数。
此时服务端的处理流程如下:

  1. 去 redis 中查找是否有 key 为 nonce:nonce 的 string
  2. 如果没有,则创建这个 key,把这个 key 失效的时间和验证 timestamp 失效的时间一致,比如是 60s。
  3. 如果有,说明这个 key 在 60s 内已经被使用了,那么这个请求就可以判断为重放请求。

这种方案nonce和timestamp参数都作为签名的一部分传到后端,基于timestamp方案可以让黑客只能在60s内进行重放攻击,加上nonce随机数以后可以保证接口只能被调用一次,可以很好的解决重放攻击问题。

代码实现

接下来以SpringBoot项目为例看看如何实现接口的防篡改和防重放功能。

1、 构建请求头对象

@Data
@Builder
public class RequestHeader 
   private String sign ;
   private Long timestamp ;
   private String nonce;

2、工具类从HttpServletRequest获取请求参数

@Slf4j
@UtilityClass
public class HttpDataUtil 
    /**
     * post请求处理:获取 Body 参数,转换为SortedMap
     *
     * @param request
     */
    public  SortedMap<String, String> getBodyParams(final HttpServletRequest request) throws IOException 
        byte[] requestBody = StreamUtils.copyToByteArray(request.getInputStream());
        String body = new String(requestBody);
        return JsonUtil.json2Object(body, SortedMap.class);
    
​
​
    /**
     * get请求处理:将URL请求参数转换成SortedMap
     */
    public static SortedMap<String, String> getUrlParams(HttpServletRequest request) 
        String param = "";
        SortedMap<String, String> result = new TreeMap<>();if (StringUtils.isEmpty(request.getQueryString())) 
            return result;
        try 
            param = URLDecoder.decode(request.getQueryString(), "utf-8");
         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
        String[] params = param.split("&");
        for (String s : params) 
            String[] array=s.split("=");
            result.put(array[0], array[1]);
        
        return result;
    

这里的参数放入SortedMap中对其进行字典排序,前端构建签名时同样需要对参数进行字典排序。

3、签名验证工具类

@Slf4j
@UtilityClass
public class SignUtil 
    /**
     * 验证签名
     * 验证算法:把timestamp + JsonUtil.object2Json(SortedMap)合成字符串,然后MD5
     */
    @SneakyThrows
    public  boolean verifySign(SortedMap<String, String> map, RequestHeader requestHeader) 
        String params = requestHeader.getNonce() + requestHeader.getTimestamp() + JsonUtil.object2Json(map);
        return verifySign(params, requestHeader);
    /**
     * 验证签名
     */
    public boolean verifySign(String params, RequestHeader requestHeader) 
        log.debug("客户端签名: ", requestHeader.getSign());
        if (StringUtils.isEmpty(params)) 
            return false;
        
        log.info("客户端上传内容: ", params);
        String paramsSign = DigestUtils.md5DigestAsHex(params.getBytes()).toUpperCase();
        log.info("客户端上传内容加密后的签名结果: ", paramsSign);
        return requestHeader.getSign().equals(paramsSign);
    

4、HttpServletRequest包装类

public class SignRequestWrapper extends HttpServletRequestWrapper 
    //用于将流保存下来
    private byte[] requestBody = null;public SignRequestWrapper(HttpServletRequest request) throws IOException 
        super(request);
        requestBody = StreamUtils.copyToByteArray(request.getInputStream());
    @Override
    public ServletInputStream getInputStream() throws IOException 
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);return new ServletInputStream() 
            @Override
            public boolean isFinished() 
                return false;
            @Override
            public boolean isReady() 
                return false;
            @Override
            public void setReadListener(ReadListener readListener) @Override
            public int read() throws IOException 
                return bais.read();
            
        ;@Override
    public BufferedReader getReader() throws IOException 
        return new BufferedReader(new InputStreamReader(getInputStream()));
    

防篡改和防重放我们会通过SpringBoot Filter来实现,而编写的filter过滤器需要读取request数据流,但是request数据流只能读取一次,需要自己实现HttpServletRequestWrapper对数据流包装,目的是将request流保存下来。

5、创建过滤器实现安全校验

@Configuration
public class SignFilterConfiguration 
    @Value("$sign.maxTime")
    private String signMaxTime;//filter中的初始化参数
    private Map<String, String> initParametersMap =  new HashMap<>();@Bean
    public FilterRegistrationBean contextFilterRegistrationBean() 
        initParametersMap.put("signMaxTime",signMaxTime);
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(signFilter());
        registration.setInitParameters(initParametersMap);
        registration.addUrlPatterns("/sign/*");
        registration.setName("SignFilter");
        // 设置过滤器被调用的顺序
        registration.setOrder(1);
        return registration;
    @Bean
    public Filter signFilter() 
        return new SignFilter();
    

@Slf4j
public class SignFilter implements Filter 
    @Resource
    private RedisUtil redisUtil;//从fitler配置中获取sign过期时间
    private Long signMaxTime;private static final String NONCE_KEY = "x-nonce-";@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException 
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
​
        log.info("过滤URL:", httpRequest.getRequestURI());HttpServletRequestWrapper requestWrapper = new SignRequestWrapper(httpRequest);
        //构建请求头
        RequestHeader requestHeader = RequestHeader.builder()
                .nonce(httpRequest.getHeader("x-Nonce"))
                .timestamp(Long.parseLong(httpRequest.getHeader("X-Time")))
                .sign(httpRequest.getHeader("X-Sign"))
                .build();//验证请求头是否存在
        if(StringUtils.isEmpty(requestHeader.getSign()) || ObjectUtils.isEmpty(requestHeader.getTimestamp()) || StringUtils.isEmpty(requestHeader.getNonce()))
            responseFail(httpResponse, ReturnCode.ILLEGAL_HEADER);
            return;
        /*
         * 1.重放验证
         * 判断timestamp时间戳与当前时间是否操过60s(过期时间根据业务情况设置),如果超过了就提示签名过期。
         */
        long now = System.currentTimeMillis() / 1000;if (now - requestHeader.getTimestamp() > signMaxTime) 
            responseFail(httpResponse,ReturnCode.REPLAY_ERROR);
            return;
        //2. 判断nonce
        boolean nonceExists = redisUtil.hasKey(NONCE_KEY + requestHeader.getNonce());
        if(nonceExists)
            //请求重复
            responseFail(httpResponse,ReturnCode.REPLAY_ERROR);
            return;
        else 
            redisUtil.set(NONCE_KEY+requestHeader.getNonce(), requestHeader.getNonce(), signMaxTime);
        
​
​
        boolean accept;
        SortedMap<String, String> paramMap;
        switch (httpRequest.getMethod())
            case "GET":
                paramMap = HttpDataUtil.getUrlParams(requestWrapper);
                accept = SignUtil.verifySign(paramMap, requestHeader);
                break;
            case "POST":
                paramMap = HttpDataUtil.getBodyParams(requestWrapper);
                accept = SignUtil.verifySign(paramMap, requestHeader);
                break;
            default:
                accept = true;
                break;
        
        if (accept) 
            filterChain.doFilter(requestWrapper, servletResponse);
         else 
            responseFail(httpResponse,ReturnCode.ARGUMENT_ERROR);
            return;
        private void responseFail(HttpServletResponse httpResponse, ReturnCode returnCode)  
        ResultData<Object> resultData = ResultData.fail(returnCode.getCode(), returnCode.getMessage());
        WebUtils.writeJson(httpResponse,resultData);
    @Override
    public void init(FilterConfig filterConfig) throws ServletException 
        String signTime = filterConfig.getInitParameter("signMaxTime");
        signMaxTime = Long.parseLong(signTime);
    

6、Redis工具类

@Component
public class RedisUtil 
    @Resource
    private RedisTemplate<String, Object> redisTemplate;/**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) 
        try 
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
         catch (Exception e) 
            e.printStackTrace();
            return false;
        
    
​
​
    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) 
        try 
            if (time > 0) 
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
             else 
                set(key, value);
            
            return true;
         catch (Exception e) 
            e.printStackTrace();
            return false;
        
    /**
     * 普通缓存放入
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) 
        try 
            redisTemplate.opsForValue().set(key, value);
            return true;
         catch (Exception e) 
            e.printStackTrace();
            return false;
        
    

总结

一般来说,暴露在外网的api接口需要做到防篡改和防重放才能称之为安全的接口。希望对大家有帮助~欢迎评论区交流!

Spring BootSpringBoot 配置文件详解

文章目录

1. 配置文件的作用

整个项目中所有重要的数据都是在配置文件中配置的,比如:

  • 数据库的连接信息
  • 项目的启动端口
  • 第三方系统的调用密钥等信息
  • 用于发现和定位问题的普通日志和异常日志

除了上述的内容,还有很多重要的属性都需要在配置文件中配置。如果想要查看 Spring Boot 更多系统配置项,Spring 的官网中已经整理好了这些信息。链接如下:https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties

2. 配置文件的格式

Spring Boot 配置文件主要分为两种格式:.properties.yml

在创建 Spring Boot 项目时,项目默认会带一个 application.properties 配置文件

properties 相当于老款的配置文件,而 yml 相当于新款的配置文件,要想使用新款的配置文件,在 /src/main/resources 目录下手动创建一个 application.yml 文件就行

补充说明:

  • 理论上 properties 和 yml 类型的配置文件可以共存,但是当这两个配置文件同时存在时,会以 properties 类型的配置文件为最高优先级,即如果这两个文件同时配置了某个属性,那么最终会使用 properties 文件的配置。当加载完 properties 类型的配置文件后,会继续加载 yml 类型的配置文件。不过并不推荐一个项目同时存在两种类型的配置文件。

  • IDEA 社区版创建好 Spring Boot 项目后,yml 配置文件默认拥有代码提示,但是 properties 配置文件没有代码提示。可以添加一个 Spring Tools 插件,解决 properties 配置文件没有代码提示的问题。

3. properties 配置文件说明

properties 是早期的配置文件的格式,也是 Spring Boot 项目创建后默认的配置文件格式。

3.1 properties 基本语法

properties 是以键值对的形式配置的,key 和 value 之间用 = 号连接。key 就是要配置的属性,value 就是要设置的值。

示例代码:

  • 配置项目的端口号

    # 配置端口号
    server.port=8080
    
  • 配置数据库连接信息

    # 配置数据库连接信息
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false
    spring.datasource.username=root
    spring.datasource.password=1234
    

可以使用 @Value 注解来读取 properties 配置文件的配置内容。读取方式为,先在注入类中创建一个相关属性,再在该属性上增加 @Value 注解,具体使用方式为 @Value(value = "$要获取的配置文件中的属性")@Value("$要获取的配置文件中的属性")

注意: 使用 @Value 注解获取配置文件的属性时要带 $

读取示例:

@Controller
@ResponseBody
public class ReadProperties 

    @Value(value = "$server.port")
    private Integer port;

    @Value("$spring.datasource.url")
    private String dbURL;

    @RequestMapping("/port")
    public Integer readPort()
        return port;
    

    @RequestMapping("/dburl")
    public String readDbURL()return dbURL;

3.2 properties 配置缺点分析

properties 是以 key=value 的形式来进行配置的,会显得代码冗余。

4. yml 配置文件说明

yml 是 YAML(YAML Ain’t a Markup Language)的缩写,译为不是一种标记语言。yml 是一种可读性高、易于理解、用于表达数据序列化的格式。它可以用于简单表达清单(数组)、散列表、标量等数据形态。是一种专门用来写配置文件的语言,可用于多种编程语言项目的文件配置,如 Java、Go、Python、C++ 等等。

4.1 yml 基本语法

yml 是树型结构的配置文件,它的基础形式是 key: value,yml 的基础语法具有以下的规则:

  • key: value 表示键值对关系,冒号后面必须有一个空格

  • 使用空格的缩进表示层级关系,空格数目不重要,只要是左对齐的一列数据,都是同一个层级的

  • 大小写敏感

示例代码:

  • 配置项目的端口号

    # 配置项目的端口号
    server:
      port: 8080
    
  • 配置数据库的连接信息

    # 配置数据库连接的信息
    spring:
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false
        username: root
        password: 1234
    

4.2 yml 配置简单的数据类型及 null

yml 配置:

# 整型
value: 10

# 布尔类型
boolean.value: true

# 字符串
string:
  value1: 你好呀 \\n aaa
  value2: '你好呀 \\n bbb'
  value3: "你好呀 \\n ccc"

# NULL
null.value: null

读取代码: 通过 @Value 注解来读取配置中的简单数据类型和 null

@Controller
@ResponseBody
public class readYml 

    @Value("$value")
    private int val1;

    @Value("$boolean.value")
    private Boolean val2;

    @Value("$string.value1")
    private String val3;

    @Value("$string.value2")
    private String val4;

    @Value("$string.value3")
    private String val5;

    @Value("$null.value")
    private Object val6;

    @RequestMapping("/read")
    public void read()
        System.out.println("整型 value:" + val1);
        System.out.println("整型 value:" + val2);
        System.out.println("整型 value:" + val3);
        System.out.println("整型 value:" + val4);
        System.out.println("整型 value:" + val5);
        System.out.println("整型 value:" + val6);
    

注意: 在 yml 中,字符串如果使用了双引号包裹,那么特殊字符是不会转义的,比如 \\n 就表示换行;而如果字符串什么都不包裹或者用单引号包裹,那么特殊字符就会自动进行转义。

4.3 yml 配置对象

yml 配置:

# 对象(写法一)
student:
  id: 1
  name: 张三
  age: 15
  
# 对象(写法二)
user: id: 2, username: 李四, password: 1234

读取代码:

首先需要创建一个匹配配置文件中对象的类,并且读取配置文件中的对象就不能使用 @Value 注解,而需要使用 @ConfigurationProperties 注解,该注解放在创建的类上,并且有一个 prefix 属性用于设置配置文件中对应的 key。创建的类中必须要有 Setter 和 Getter 方法。

@Component
@ConfigurationProperties(prefix = "student")
public class Student 
    private int id;
    private String name;
    private int age;

    public int getId() 
        return id;
    

    public void setId(int id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public int getAge() 
        return age;
    

    public void setAge(int age) 
        this.age = age;
    

    @Override
    public String toString() 
        return "Student" +
                "id=" + id +
                ", name='" + name + '\\'' +
                ", age=" + age +
                '';
    

通过创建的类读取到配置文件的对象后,将该类注册到 Spring 容器中,当使用时,直接通过 @Autowired 或者 @Resource 注解注入就好。

@Controller
public class readYml 

    @Autowired
    private Student student;

    @RequestMapping("/student")
    public void read()
        System.out.println(student);
    

4.4 yml 配置 list 集合

yml 配置:

# 集合(方式一)
dbtypes1:
  fruits:
    - 苹果
    - 菠萝
    - 西瓜

# 集合(方式二)
dbtypes2: color: [红色,蓝色]

读取代码:

yml 配置集合时,也需要通过 @ConfigurationProperties 注解来读取,该注解的 prefix 属性对应着上述 yml 配置的 dbtypes1 或者 dbtypes2。接下来创建一个匹配配置文件中集合的类

@Component
@ConfigurationProperties(prefix = "dbtypes1")
public class DBTypes 
    private List<String> fruits;

    public List<String> getFruits() 
        return fruits;
    

    public void setFruits(List<String> fruits) 
        this.fruits = fruits;
    

通过创建的类读取到配置文件的对象后,将该类注册到 Spring 容器中,当使用时,直接通过 @Autowired 或者 @Resource 注解注入就好。

@Controller
public class readYml 

    @Resource
    private DBTypes dbTypes;

    @RequestMapping("/dbtypes")
    public void read()
        System.out.println(dbTypes.getFruits());
    

5. properties 和 yml 的总结

  • properties 是以 key=value 的形式配置的键值类型的配置⽂件,⽽ yml 使⽤的是类似 json 格式的 树形配置⽅式进⾏配置的,yml 层级之间使⽤换⾏缩进的⽅式配置,key 和 value 之间使⽤“: ”英⽂ 冒号加空格的⽅式设置,并且空格不可省略。
  • properties 为早期并且默认的配置⽂件格式,但其配置存在⼀定的冗余数据,使⽤ yml 可以很好的 解决数据冗余的问题。
  • yml 通⽤性更好,⽀持更多语⾔,如 Java、Go、Python 等,如果是云服务器开发,可以使⽤⼀份 配置⽂件作为 Java 和 Go 的共同配置⽂件。
  • yml 虽然可以和 properties 共存,但⼀个项⽬中建议使⽤统⼀的配置类型⽂件。
  • yml 支持更多的数据类型

6. Spring Boot 读取配置文件的方法

Spring Boot 中读取配置文件有以下5种方法:

  • 使用 @Value 读取配置文件
  • 使用 @ConfigurationProperties 读取配置文件
  • 使用 Environment 读取配置文件
  • 使用 @PropertySource 读取配置文件
  • 使用原生方式读取配置文件

它们的具体使用方式如下,为了方便测试,接下来将对下面的 application.yml 配置文件进行读取。

yml: 
  name: yml
  desc: configuration

6.1 使用 @Value 读取配置文件

使用 @Value 可以读取单个配置项,代码如下所示:

@Controller
@ResponseBody
public class readYml 

    @Value("$yml.name")
    private String name;

    @RequestMapping("/read1")
    public void read1()
        System.out.println(name);
    

6.2 使用 @ConfigurationProperties 读取配置文件

使用 @ConfigurationProperties 可以按对象或者集合的形式来读取配置项,首先先创建一个和配置中对象和集合匹配的类,然后通过@ConfigurationProperties 读取配置文件中的对象或集合,然后注入到 Spring 中,当需要使用时,通过 @Autowired 或者 @Resource 注入就可以获取到,代码如下所示:

@Component
@ConfigurationProperties(prefix = "yml")
public class Yml 
    private String name;
    private String desc;

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public String getDesc() 
        return desc;
    

    public void setDesc(String desc) 
        this.desc = desc;
    

    @Override
    public String toString() 
        return "Yml" +
                "name='" + name + '\\'' +
                ", desc='" + desc + '\\'' +
                '';
    

@Controller
@ResponseBody
public class readYml 

    @Autowired
    private Yml yml;

    @RequestMapping("/read2")
    public void read2()
        System.out.println(yml);
    

6.3 使用 Environment 读取配置文件

Environment 是 Spring Core 中的一个用于读取配置文件的类,将此类使用 @Autowired 或者 @Resource 注入到类中就可以使用它的 getProperty 方法来获取某个配置项的值,代码如下所示:

@Controller
@ResponseBody
public class readYml 

    @Resource
    private Environment enviroment;

    @RequestMapping("/read3")
    public void read3()
        System.out.println(enviroment.getProperty("yml.name"));
    


6.4 使用 @PropertySource 读取配置文件

使用 @PropertySource 注解可以来指定读取某个配置文件,比如指定读取 application.yml 配置文件的内容,就可以在指定的类上添加 @PropertySource("classpath:application.yml"),代码如下所示:

@Controller
@ResponseBody
@PropertySource("classpath:application.yml")
public class readYml 

    @Value("$yml.desc")
    private String desc;

    @RequestMapping("/read4")
    public void read4()
        System.out.println(desc);
    

6.5 使用原生方式 Properties 对象读取配置文件

可以使用最原始的方式 Properties 对象来读取配置文件,代码如下所示:
注意: 使用原生方式读取配置文件,只支持 properties 格式的配置文件

@Controller
@ResponseBody
public class readYml 

    @RequestMapping("/read5")
    public void read5() 
        Properties properties = new Properties();
        InputStreamReader inputStreamReader = new InputStreamReader(
                this.getClass().getClassLoader().getResourceAsStream("application.properties"),
                StandardCharsets.UTF_8
        );
        try 
            properties.load(inputStreamReader);
         catch (IOException e) 
            e.printStackTrace();
        
        System.out.println(properties.getProperty("properties.name"));
    

以上是关于Spring BootSpringBoot 如何保证接口安全?老鸟们都是这么玩的!的主要内容,如果未能解决你的问题,请参考以下文章

Spring BootSpringBoot 配置文件详解

Spring Bootspringboot中自定义配置项

Spring Bootspringboot中自定义配置项

Spring Bootspringboot中自定义配置项

Spring BootSpringBoot参数验证以及实现原理

Spring BootSpringBoot中系统多环境配置