SpringBoot 接口加密解密

手机软件开发 2024-9-24 11:43:55 95 0 来自 中国
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.exception;import lombok.Getter;import java.util.List;/** * @author HLH * @description 自定义参数异常 * @email 17703595860@163.com * @date Created in 2021/8/10 下战书10:56 */@Getterpublic class ParamException extends Exception {    private final List<String> fieldList;    private final List<String> msgList;    public ParamException(List<String> fieldList, List<String> msgList) {        this.fieldList = fieldList;        this.msgList = msgList;    }}ValidationUtils
package xyz.hlh.cryptotest.utils;import xyz.hlh.cryptotest.exception.CustomizeException;import xyz.hlh.cryptotest.exception.ParamException;import javax.validation.ConstraintViolation;import javax.validation.Validation;import javax.validation.Validator;import java.util.LinkedList;import java.util.List;import java.util.Set;/** * @author HLH * @description 验证工具类 * @email 17703595860@163.com * @date Created in 2021/8/10 下战书10:56 */public class ValidationUtils {    private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();    /**     * 验证数据     * @param object 数据     */    public static void validate(Object object) throws CustomizeException {        Set<ConstraintViolation<Object>> validate = VALIDATOR.validate(object);        // 验证结果异常        throwParamException(validate);    }    /**     * 验证数据(分组)     * @param object 数据     * @param groups 所在组     */    public static void validate(Object object, Class<?> ... groups) throws CustomizeException {        Set<ConstraintViolation<Object>> validate = VALIDATOR.validate(object, groups);        // 验证结果异常        throwParamException(validate);    }    /**     * 验证数据中的某个字段(分组)     * @param object 数据     * @param propertyName 字段名称     */    public static void validate(Object object, String propertyName) throws CustomizeException {        Set<ConstraintViolation<Object>> validate = VALIDATOR.validateProperty(object, propertyName);        // 验证结果异常        throwParamException(validate);    }    /**     * 验证数据中的某个字段(分组)     * @param object 数据     * @param propertyName 字段名称     * @param groups 所在组     */    public static void validate(Object object, String propertyName, Class<?> ... groups) throws CustomizeException {        Set<ConstraintViolation<Object>> validate = VALIDATOR.validateProperty(object, propertyName, groups);        // 验证结果异常        throwParamException(validate);    }    /**     * 验证结果异常     * @param validate 验证结果     */    private static void throwParamException(Set<ConstraintViolation<Object>> validate) throws CustomizeException {        if (validate.size() > 0) {            List<String> fieldList = new LinkedList<>();            List<String> msgList = new LinkedList<>();            for (ConstraintViolation<Object> next : validate) {                fieldList.add(next.getPropertyPath().toString());                msgList.add(next.getMessage());            }            throw new ParamException(fieldList, msgList);        }    }}2.4 自定义starter

自定义starter步调

  • 创建工厂,编写功能代码
  • 声明主动设置类,把须要对外提供的对象创建好,通过设置类同一向外袒露
  • 在resource目次下准备一个名为spring/spring.factories的文件,以org.springframework.boot.autoconfigure.EnableAutoConfiguration为key,主动设置类为value列表,举行注册
2.5 RequestBodyAdvice和ResponseBodyAdvice


  • RequestBodyAdvice是对请求的json串举行处置惩罚, 一样寻常利用环境是处置惩罚接口参数的主动解密
  • ResponseBodyAdvice是对请求相应的jsoin传举行处置惩罚,一样寻常用于相应结果的加密
3. 功能介绍

接口相应数据的时间,返回的是加密之后的数据 接口入参的时间,接收的是解密之后的数据,但是在进入接口之前,会主动解密,取得对应的数据
4. 功能细节

加密解密利用对称加密的AES算法,利用hutool-crypto模块举行实现
全部的实体类提取一个公共父类,包罗属性时间戳,用于加密数据返回之后的实效性,如果凌驾60分钟,那么其他接口将不举行处置惩罚。
如果接口加了加密注解EncryptionAnnotation,而且返回同一的json数据Result类,则主动对数据举行加密。如果是继承了同一父类RequestBase的数据,主动注入时间戳,确保数据的时效性
如果接口加相识密注解DecryptionAnnotation,而且参数利用RequestBody注解标注,传入json利用同一格式RequestData类,而且内容是继承了包罗时间长的父类RequestBase,则主动解密,而且转为对应的数据类型
功能提供Springboot的starter,实现开箱即用
5. 代码实现

https://gitee.com/springboot-hlh/spring-boot-csdn/tree/master/09-spring-boot-interface-crypto
5.1 项目布局

5.2 crypto-common

5.2.1 布局

5.3 crypto-spring-boot-starter

5.3.1 接口

5.3.2 告急代码

crypto.properties AES须要的参数设置
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 20:21, Processed in 0.161714 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表