SpringBoot 接口加密解密,新姿势

1. 介绍

在我们日常的Java开发中,免不了和其他系统的业务交互,或者微服务之间的接口调用

如果我们想保证数据传输的安全,对接口出参加密,入参解密。

但是不想写重复代码,我们可以提供一个通用starter,提供通用加密解密功能

2. 前置知识

2.1 hutool-crypto加密解密工具

hutool-crypto提供了很多加密解密工具,包括对称加密,非对称加密,摘要加密等等,这不做详细介绍。

2.2 request流只能读取一次的问题

2.2.1 问题:

在接口调用链中,request的请求流只能调用一次,处理之后,如果之后还需要用到请求流获取数据,就会发现数据为空。

比如使用了filter或者aop在接口处理之前,获取了request中的数据,对参数进行了校验,那么之后就不能在获取request请求流了

2.2.2 解决办法

继承HttpServletRequestWrapper,将请求中的流copy一份,复写getInputStream和getReader方法供外部使用。每次调用后的getInputStream方法都是从复制出来的二进制数组中进行获取,这个二进制数组在对象存在期间一致存在。

使用Filter过滤器,在一开始,替换request为自己定义的可以多次读取流的request。

这样就实现了流的重复获取

InputStreamHttpServletRequestWrapper
package xyz.hlh.cryptotest.utils;

import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 请求流支持多次获取
 */
public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {

    /**
     * 用于缓存输入流
     */
    private ByteArrayOutputStream cachedBytes;

    public InputStreamHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (cachedBytes == null) {
            // 首次获取流时,将流放入 缓存输入流 中
            cacheInputStream();
        }

        // 从 缓存输入流 中获取流并返回
        return new CachedServletInputStream(cachedBytes.toByteArray());
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 首次获取流时,将流放入 缓存输入流 中
     */
    private void cacheInputStream() throws IOException {
        // 缓存输入流以便多次读取。为了方便, 我使用 org.apache.commons IOUtils
        cachedBytes = new ByteArrayOutputStream();
        IOUtils.copy(super.getInputStream(), cachedBytes);
    }

    /**
     * 读取缓存的请求正文的输入流
     * <p>
     * 用于根据 缓存输入流 创建一个可返回的
     */
    public static class CachedServletInputStream extends ServletInputStream {

        private final ByteArrayInputStream input;

        public CachedServletInputStream(byte[] buf) {
            // 从缓存的请求正文创建一个新的输入流
            input = new ByteArrayInputStream(buf);
        }

        @Override
        public boolean isFinished() {
            return false;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener listener) {

        }

        @Override
        public int read() throws IOException {
            return input.read();
        }
    }

}
HttpServletRequestInputStreamFilter
package xyz.hlh.cryptotest.filter;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import xyz.hlh.cryptotest.utils.InputStreamHttpServletRequestWrapper;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;

/**
 * @author HLH
 * @description:
 *      请求流转换为多次读取的请求流 过滤器
 * @email 17703595860@163.com
 * @date : Created in 2022/2/4 9:58
 */
@Component
@Order(HIGHEST_PRECEDENCE + 1)  // 优先级最高
public class HttpServletRequestInputStreamFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        // 转换为可以多次获取流的request
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        InputStreamHttpServletRequestWrapper inputStreamHttpServletRequestWrapper = new InputStreamHttpServletRequestWrapper(httpServletRequest);

        // 放行
        chain.doFilter(inputStreamHttpServletRequestWrapper, response);
    }
}

2.3 SpringBoot的参数校验validation

为了减少接口中,业务代码之前的大量冗余的参数校验代码

SpringBoot-validation提供了优雅的参数校验,入参都是实体类,在实体类字段上加上对应注解,就可以在进入方法之前,进行参数校验,如果参数错误,会抛出错误BindException,是不会进入方法的。

这种方法,必须要求在接口参数上加注解@Validated或者是@Valid

但是很多清空下,我们希望在代码中调用某个实体类的校验功能,所以需要如下工具类

ParamException
package xyz.hlh.cryptotest.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,可以使用RequestBodyAdvice和ResponseBodyAdvice来实现接口加密解密逻辑。这两个接口可以在方法中使用@RequestBody和@ResponseBody注解时生效。具体的实现步骤如下: 1. 创建一个自动配置类,比如AppConfig,用于配置加密解密的相关参数。在该类中,可以使用@Bean注解创建一个AES对象,用于进行加密解密操作。可以参考\[3\]中的示例代码。 2. 在自动配置类中,可以使用@Configuration注解标记该类为配置类,并使用@Resource注解注入CryptConfig对象,该对象用于获取加密解密的相关配置参数。 3. 在加密解密的逻辑中,可以使用AES对象进行加密解密操作。可以根据具体的需求选择合适的加密模式、填充方式、密钥和向量等参数。 4. 在接口方法中,使用@RequestBody注解标记需要加密的请求数据,在方法中进行解密操作。然后处理相应的业务逻辑,并使用@ResponseBody注解标记需要加密的返回数据,在返回之前进行加密操作。 通过以上步骤,可以将加密解密的逻辑提取出来,使接口方法只关注业务逻辑的处理。具体的实现可以参考\[2\]中的项目结构示例代码。 总结起来,使用Spring Boot的RequestBodyAdvice和ResponseBodyAdvice可以很方便地实现接口加密解密逻辑,提高代码的可维护性和安全性。 #### 引用[.reference_title] - *1* [springboot中如何优雅的对接口数据进行加密解密](https://blog.csdn.net/xinghui_liu/article/details/121208804)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [SpringBoot 接口加密解密姿势!](https://blog.csdn.net/qq_42914528/article/details/128168527)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值