前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【开发者必看】Spring Boot接口加密解密新姿势,技术干货,开箱即用!

【开发者必看】Spring Boot接口加密解密新姿势,技术干货,开箱即用!

作者头像
程序视点
发布于 2025-01-17 12:06:44
发布于 2025-01-17 12:06:44
21800
代码可运行
举报
文章被收录于专栏:程序小小事程序小小事
运行总次数: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一份,复写getInputStreamgetReader方法供外部使用。

每次调用后的getInputStream方法都是从复制出来的二进制数组中进行获取,这个二进制数组在对象存在期间一直存在。

使用Filter过滤器,在一开始,替换request为自己定义的可以多次读取流的request。这样就实现了流的重复获取。

对以下代码实现或示例不清楚的小伙伴,可以安装、激活GitHub Copilot智能编程助手。本号提供全网唯一真正做到100%赔付保障的GitHub Copilot激活!

关注WX公众号【程序视点】,回复copilot,进行了解和订阅!

InputStreamHttpServletRequestWrapper

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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  
 */  
@Getter  
public 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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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需要的参数配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 模式    cn.hutool.crypto.Mode
crypto.mode=CTS
# 补码方式 cn.hutool.crypto.Mode
crypto.padding=PKCS5Padding
# 秘钥
crypto.key=testkey123456789
# 盐
crypto.iv=testiv1234567890

spring.factories 自动配置文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\  
        xyz.hlh.crypto.config.AppConfig  

CryptConfig AES需要的配置参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package xyz.hlh.crypto.config;  
  
import cn.hutool.crypto.Mode;  
import cn.hutool.crypto.Padding;  
import lombok.Data;  
import lombok.EqualsAndHashCode;  
import lombok.Getter;  
import org.springframework.boot.context.properties.ConfigurationProperties;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.PropertySource;  
  
import java.io.Serializable;  
  
/**  
 * @author HLH  
 * @description: AES需要的配置参数  
 * @email 17703595860@163.com  
 * @date : Created in 2022/2/4 13:16  
 */  
@Configuration  
@ConfigurationProperties(prefix = "crypto")  
@PropertySource("classpath:crypto.properties")  
@Data  
@EqualsAndHashCode  
@Getter  
public class CryptConfig implements Serializable {  
  
    private Mode mode;  
    private Padding padding;  
    private String key;  
    private String iv;  
  
}  

AppConfig 自动配置类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package xyz.hlh.crypto.config;  
  
import cn.hutool.crypto.symmetric.AES;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
  
import javax.annotation.Resource;  
import java.nio.charset.StandardCharsets;  
  
/**  
 * @author HLH  
 * @description: 自动配置类  
 * @email 17703595860@163.com  
 * @date : Created in 2022/2/4 13:12  
 */  
@Configuration  
public class AppConfig {  
  
    @Resource  
    private CryptConfig cryptConfig;  
  
    @Bean  
    public AES aes() {  
        return new AES(cryptConfig.getMode(), cryptConfig.getPadding(), cryptConfig.getKey().getBytes(StandardCharsets.UTF_8), cryptConfig.getIv().getBytes(StandardCharsets.UTF_8));  
    }  
  
}  

DecryptRequestBodyAdvice请求自动解密

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package xyz.hlh.crypto.advice;  
  
import com.fasterxml.jackson.databind.ObjectMapper;  
import lombok.SneakyThrows;  
import org.apache.commons.lang3.StringUtils;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.core.MethodParameter;  
import org.springframework.http.HttpInputMessage;  
import org.springframework.http.converter.HttpMessageConverter;  
import org.springframework.web.bind.annotation.ControllerAdvice;  
import org.springframework.web.context.request.RequestAttributes;  
import org.springframework.web.context.request.RequestContextHolder;  
import org.springframework.web.context.request.ServletRequestAttributes;  
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;  
import xyz.hlh.crypto.annotation.DecryptionAnnotation;  
import xyz.hlh.crypto.common.exception.ParamException;  
import xyz.hlh.crypto.constant.CryptoConstant;  
import xyz.hlh.crypto.entity.RequestBase;  
import xyz.hlh.crypto.entity.RequestData;  
import xyz.hlh.crypto.util.AESUtil;  
  
import javax.servlet.ServletInputStream;  
import javax.servlet.http.HttpServletRequest;  
import java.io.IOException;  
import java.lang.reflect.Type;  
  
/**  
 * @author HLH  
 * @description: requestBody 自动解密  
 * @email 17703595860@163.com  
 * @date : Created in 2022/2/4 15:12  
 */  
@ControllerAdvice  
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {  
  
    @Autowired  
    private ObjectMapper objectMapper;  
  
    /**  
     * 方法上有DecryptionAnnotation注解的,进入此拦截器  
     * @param methodParameter 方法参数对象  
     * @param targetType 参数的类型  
     * @param converterType 消息转换器  
     * @return true,进入,false,跳过  
     */  
    @Override  
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {  
        return methodParameter.hasMethodAnnotation(DecryptionAnnotation.class);  
    }  
  
    @Override  
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {  
        return inputMessage;  
    }  
  
    /**  
     * 转换之后,执行此方法,解密,赋值  
     * @param body spring解析完的参数  
     * @param inputMessage 输入参数  
     * @param parameter 参数对象  
     * @param targetType 参数类型  
     * @param converterType 消息转换类型  
     * @return 真实的参数  
     */  
    @SneakyThrows  
    @Override  
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {  
  
        // 获取request  
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();  
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;  
        if (servletRequestAttributes == null) {  
            throw new ParamException("request错误");  
        }  
  
        HttpServletRequest request = servletRequestAttributes.getRequest();  
  
        // 获取数据  
        ServletInputStream inputStream = request.getInputStream();  
        RequestData requestData = objectMapper.readValue(inputStream, RequestData.class);  
  
        if (requestData == null || StringUtils.isBlank(requestData.getText())) {  
            throw new ParamException("参数错误");  
        }  
  
        // 获取加密的数据  
        String text = requestData.getText();  
  
        // 放入解密之前的数据  
        request.setAttribute(CryptoConstant.INPUT_ORIGINAL_DATA, text);  
  
        // 解密  
        String decryptText = null;  
        try {  
            decryptText = AESUtil.decrypt(text);  
        } catch (Exception e) {  
            throw new ParamException("解密失败");  
        }  
  
        if (StringUtils.isBlank(decryptText)) {  
            throw new ParamException("解密失败");  
        }  
  
        // 放入解密之后的数据  
        request.setAttribute(CryptoConstant.INPUT_DECRYPT_DATA, decryptText);  
  
        // 获取结果  
        Object result = objectMapper.readValue(decryptText, body.getClass());  
  
        // 强制所有实体类必须继承RequestBase类,设置时间戳  
        if (result instanceof RequestBase) {  
            // 获取时间戳  
            Long currentTimeMillis = ((RequestBase) result).getCurrentTimeMillis();  
            // 有效期 60秒  
            long effective = 60*1000;  
  
            // 时间差  
            long expire = System.currentTimeMillis() - currentTimeMillis;  
  
            // 是否在有效期内  
            if (Math.abs(expire) > effective) {  
                throw new ParamException("时间戳不合法");  
            }  
  
            // 返回解密之后的数据  
            return result;  
        } else {  
            throw new ParamException(String.format("请求参数类型:%s 未继承:%s", result.getClass().getName(), RequestBase.class.getName()));  
        }  
    }  
  
    /**  
     * 如果body为空,转为空对象  
     * @param body spring解析完的参数  
     * @param inputMessage 输入参数  
     * @param parameter 参数对象  
     * @param targetType 参数类型  
     * @param converterType 消息转换类型  
     * @return 真实的参数  
     */  
    @SneakyThrows  
    @Override  
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {  
        String typeName = targetType.getTypeName();  
        Class<?> bodyClass = Class.forName(typeName);  
        return bodyClass.newInstance();  
    }  
}  

EncryptResponseBodyAdvice相应自动加密

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package xyz.hlh.crypto.advice;  
  
import cn.hutool.json.JSONUtil;  
import com.fasterxml.jackson.databind.ObjectMapper;  
import lombok.SneakyThrows;  
import org.apache.commons.lang3.StringUtils;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.core.MethodParameter;  
import org.springframework.http.MediaType;  
import org.springframework.http.ResponseEntity;  
import org.springframework.http.converter.HttpMessageConverter;  
import org.springframework.http.server.ServerHttpRequest;  
import org.springframework.http.server.ServerHttpResponse;  
import org.springframework.web.bind.annotation.ControllerAdvice;  
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;  
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;  
import xyz.hlh.crypto.annotation.EncryptionAnnotation;  
import xyz.hlh.crypto.common.entity.Result;  
import xyz.hlh.crypto.common.exception.CryptoException;  
import xyz.hlh.crypto.entity.RequestBase;  
import xyz.hlh.crypto.util.AESUtil;  
  
import java.lang.reflect.Type;  
  
/**  
 * @author HLH  
 * @description:  
 * @email 17703595860@163.com  
 * @date : Created in 2022/2/4 15:12  
 */  
@ControllerAdvice  
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Result<?>> {  
  
    @Autowired  
    private ObjectMapper objectMapper;  
  
    @Override  
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {  
        ParameterizedTypeImpl genericParameterType = (ParameterizedTypeImpl)returnType.getGenericParameterType();  
  
        // 如果直接是Result,则返回  
        if (genericParameterType.getRawType() == Result.class && returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {  
            return true;  
        }  
  
        if (genericParameterType.getRawType() != ResponseEntity.class) {  
            return false;  
        }  
  
        // 如果是ResponseEntity<Result>  
        for (Type type : genericParameterType.getActualTypeArguments()) {  
            if (((ParameterizedTypeImpl) type).getRawType() == Result.class && returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {  
                return true;  
            }  
        }  
  
        return false;  
    }  
  
    @SneakyThrows  
    @Override  
    public Result<?> beforeBodyWrite(Result<?> body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {  
  
        // 加密  
        Object data = body.getData();  
  
        // 如果data为空,直接返回  
        if (data == null) {  
            return body;  
        }  
  
        // 如果是实体,并且继承了Request,则放入时间戳  
        if (data instanceof RequestBase) {  
            ((RequestBase)data).setCurrentTimeMillis(System.currentTimeMillis());  
        }  
  
        String dataText = JSONUtil.toJsonStr(data);  
  
        // 如果data为空,直接返回  
        if (StringUtils.isBlank(dataText)) {  
            return body;  
        }  
  
        // 如果位数小于16,报错  
        if (dataText.length() < 16) {  
            throw new CryptoException("加密失败,数据小于16位");  
        }  
  
        String encryptText = AESUtil.encryptHex(dataText);  
  
        return Result.builder()  
                .status(body.getStatus())  
                .data(encryptText)  
                .message(body.getMessage())  
                .build();  
    }  
}  

5.4 crypto-test

5.4.1 结构

5.4.2 重要代码application.yml 配置文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spring:  
        mvc:  
        format:  
        date-time: yyyy-MM-dd HH:mm:ss  
        date: yyyy-MM-dd  
        # 日期格式化  
        jackson:  
        date-format: yyyy-MM-dd HH:mm:ss  

Teacher 实体类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package xyz.hlh.crypto.entity;  
  
import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.EqualsAndHashCode;  
import lombok.NoArgsConstructor;  
import org.hibernate.validator.constraints.Range;  
  
import javax.validation.constraints.NotBlank;  
import javax.validation.constraints.NotNull;  
import java.io.Serializable;  
import java.util.Date;  
  
/**  
 * @author HLH  
 * @description: Teacher实体类,使用SpringBoot的validation校验  
 * @email 17703595860@163.com  
 * @date : Created in 2022/2/4 10:21  
 */  
@Data  
@NoArgsConstructor  
@AllArgsConstructor  
@EqualsAndHashCode(callSuper = true)  
public class Teacher extends RequestBase implements Serializable {  
  
    @NotBlank(message = "姓名不能为空")  
    private String name;  
    @NotNull(message = "年龄不能为空")  
    @Range(min = 0, max = 150, message = "年龄不合法")  
    private Integer age;  
    @NotNull(message = "生日不能为空")  
    private Date birthday;  
  
}  

TestController 测试Controller

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package xyz.hlh.crypto.controller;  
  
import org.springframework.http.ResponseEntity;  
import org.springframework.validation.annotation.Validated;  
import org.springframework.web.bind.annotation.PostMapping;  
import org.springframework.web.bind.annotation.RequestBody;  
import org.springframework.web.bind.annotation.RestController;  
import xyz.hlh.crypto.annotation.DecryptionAnnotation;  
import xyz.hlh.crypto.annotation.EncryptionAnnotation;  
import xyz.hlh.crypto.common.entity.Result;  
import xyz.hlh.crypto.common.entity.ResultBuilder;  
import xyz.hlh.crypto.entity.Teacher;  
  
/**  
 * @author HLH  
 * @description: 测试Controller  
 * @email 17703595860@163.com  
 * @date : Created in 2022/2/4 9:16  
 */  
@RestController  
public class TestController implements ResultBuilder {  
  
    /**  
     * 直接返回对象,不加密  
     * @param teacher Teacher对象  
     * @return 不加密的对象  
     */  
    @PostMapping("/get")  
    public ResponseEntity<Result<?>> get(@Validated @RequestBody Teacher teacher) {  
        return success(teacher);  
    }  
  
    /**  
     * 返回加密后的数据  
     * @param teacher Teacher对象  
     * @return 返回加密后的数据 ResponseBody<Result>格式  
     */  
    @PostMapping("/encrypt")  
    @EncryptionAnnotation  
    public ResponseEntity<Result<?>> encrypt(@Validated @RequestBody Teacher teacher) {  
        return success(teacher);  
    }  
  
    /**  
     * 返回加密后的数据  
     * @param teacher Teacher对象  
     * @return 返回加密后的数据 Result格式  
     */  
    @PostMapping("/encrypt1")  
    @EncryptionAnnotation  
    public Result<?> encrypt1(@Validated @RequestBody Teacher teacher) {  
        return success(teacher).getBody();  
    }  
  
    /**  
     * 返回解密后的数据  
     * @param teacher Teacher对象  
     * @return 返回解密后的数据  
     */  
    @PostMapping("/decrypt")  
    @DecryptionAnnotation  
    public ResponseEntity<Result<?>> decrypt(@Validated @RequestBody Teacher teacher) {  
        return success(teacher);  
    }  
  
}

好了!关于SpringBoot接口加解密的介绍就到这里了!有需要的小伙伴,可以直接上手试试!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-11-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序视点 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
图片转base64 并根据格式加前缀
这是一个使用Java编写的示例代码,用于将图片文件转换为Base64编码并添加特定格式的前缀。代码中的 imageToBase64WithPrefix 方法接受图片路径、图片格式和前缀作为参数,然后读取图片文件的字节数据,将其转换为Base64编码,并将前缀添加到编码后的字符串中。主方法演示了如何调用这个方法并输出得到的带有前缀的Base64编码。请替换示例代码中的路径、格式和前缀以适应您的实际需求。
默 语
2024/11/20
2550
图片转base64 并根据格式加前缀
base64 和音频文件 互相转换
public static String encodeBase64File(String path) throws Exception { File file = new File(p
用户5899361
2020/12/07
3.4K0
documents4j 文档转换
https://github.com/documents4j/documents4j
鱼找水需要时间
2023/08/27
7951
documents4j 文档转换
基于POI的Word解析成HTML(base64图片)
我们一般提交文档常采用的是富文本编辑上传的常规方法,有时候想将文档上传后,再进行富文本编辑怎么办呢?
CodeWwang
2022/08/24
1.7K0
【密码学】Base64 编码 ( Base64 简介 | Base64 编码原理 | 最后编码组字节不足时补位 ‘=‘ 符号 | Base64 编码实现参考 )
Base64 不是加密算法 , 是一种 可读性算法 , 其目的不是用于保护数据 , 其目的是为了可读性 ;
韩曙亮
2023/03/29
3.6K0
【密码学】Base64 编码 ( Base64 简介 | Base64 编码原理 | 最后编码组字节不足时补位 ‘=‘ 符号 | Base64 编码实现参考 )
Java实现word、excel、ppt、txt等办公文件在线预览功能
Java实现办公文件在线预览功能是一个大家在工作中也许会遇到的需求,网上些公司专门提供这样的服务,不过需要收费。
Java旅途
2021/11/26
2.9K0
Java Base64转换,Java Base64工具类
但这个性能一般,而且转换出来的base64字符串会有换行符,可能还需要替换换行符,避免在某些场景因为分行导致出错 2、使用Jdk8的Base64工具类(优先考虑使用)
用户9131103
2023/07/17
7350
Python中String, Bytes, Hex, Base64之间的关系与转换方法详解
In this program, you are required to learn basic concepts of Python 3.
timerring
2022/07/20
7720
Python中String, Bytes, Hex, Base64之间的关系与转换方法详解
图片美化增强AI接口调用手册
POST BODY,接口要求以Post body方式发送,因为要传base64字符串,请求参数过长有400错误的
SmileNicky
2019/06/14
9500
图片美化增强AI接口调用手册
word,ppt,excel转pdf,pdf转html工具类搭建
我看到很多需求要求word,excel,ppt,pptx转pdf等工具类。还有就是pdf转图片转html这里介绍一个这个工具类。
斯文的程序
2019/11/07
3.6K0
Illegal base64 character a
base64解码为文件方式不止一种,下面两种,如果一种报错,换另外一种试一下: byte[] bytes = Base64Utils.decodeFromString(base64Str); byte[] bytes1 = new BASE64Decoder().decodeBuffer(base64Str); 先将base64字符串转为byte数组,然后再转为文件: public static File base64StrToFile(Strin
IT云清
2019/01/22
2.9K0
Java 图片URL转Base64编码
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/10/05
3.8K0
Java 图片URL转Base64编码
【小家java】java8新特性之---Base64加密和解密原理
Base64是一种字符串编码格式,采用了A-Z,a-z,0-9,“+”和“/”这64个字符来编码原始字符(还有垫字符“=”)。一个字符本身是一个字节,也就是8位,而base64编码后的一个字符只能表示6位的信息。也就是原始字符串中的3字节的信息编码会变成4字节的信息。Base64的主要作用是满足MIME的传输需求。 在Java8中Base64编码已经成为Java类库的标准,且内置了Base64编码的编码器和解码器。
YourBatman
2019/09/03
1.4K0
PDF技术 -Java实现Html转PDF文件
综合:使用WKHtmlToPdf效果(样式)最好。但速度较慢(对于文件来说)。其余均有大大小小的失真问题。
用户1518699
2022/05/09
13K0
PDF技术 -Java实现Html转PDF文件
.net pdf转word_pdf to word
文件的详细路径: pdfToDoc(String pdfPath, String docPath) 输入流: pdfToDoc(InputStream pdfPathInputStream, String docPath)
全栈程序员站长
2022/11/17
7.3K0
.net pdf转word_pdf to word
springboot中使用SFTP文件上传
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/03
3.4K0
C语言实现base64编解码
base64编解码 工作中经常会用到base64编解码, 有些开源库中也有实现, 但是如果再去看他们的怎么用有时候也是有点费劲的, 还有就是需要引用那个头文件啊, 什么的, 尤其是OpenSSL里边的, 所以这里献上原理, 及其使用. 至于用途还有详细的介绍我觉得某度某科里讲的挺好的, 这里就只写上实现原理及代码了. 详细请看base64.h 和base64.c, 使用见main.c 即可, 可以使用任何编译器编译运行, 下面依次是base64.h, base64.c, main.c如果需要
战神伽罗
2019/07/24
4.3K0
Java原生图片Base64转码与Base64解码
程序员朱永胜
2023/08/21
3660
Java 8中的Base64编码和解码
Java 8会因为将lambdas,流,新的日期/时间模型和Nashorn JavaScript引擎引入Java而被记住。有些人还会记得Java 8,因为它引入了各种小但有用的功能,例如Base64 API。什么是Base64以及如何使用此API?这篇文章回答了这些问题。
银河1号
2019/04/11
5.6K1
Android Base64编码字符串与文件之间的转化
开发中有时传输图片或音频等文件。我们会采用Base64编码成字符串传输 那么获取后 就要重新生成文件。
码客说
2019/10/22
2.5K0
推荐阅读
相关推荐
图片转base64 并根据格式加前缀
更多 >
LV.3
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档