• 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

    1. package xyz.hlh.cryptotest.utils;
    2. import org.apache.commons.io.IOUtils;
    3. import javax.servlet.ReadListener;
    4. import javax.servlet.ServletInputStream;
    5. import javax.servlet.http.HttpServletRequest;
    6. import javax.servlet.http.HttpServletRequestWrapper;
    7. import java.io.BufferedReader;
    8. import java.io.ByteArrayInputStream;
    9. import java.io.ByteArrayOutputStream;
    10. import java.io.IOException;
    11. import java.io.InputStreamReader;
    12. /**
    13.  * 请求流支持多次获取
    14.  */
    15. public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {
    16.     /**
    17.      * 用于缓存输入流
    18.      */
    19.     private ByteArrayOutputStream cachedBytes;
    20.     public InputStreamHttpServletRequestWrapper(HttpServletRequest request) {
    21.         super(request);
    22.     }
    23.     @Override
    24.     public ServletInputStream getInputStream() throws IOException {
    25.         if (cachedBytes == null) {
    26.             // 首次获取流时,将流放入 缓存输入流 中
    27.             cacheInputStream();
    28.         }
    29.         // 从 缓存输入流 中获取流并返回
    30.         return new CachedServletInputStream(cachedBytes.toByteArray());
    31.     }
    32.     @Override
    33.     public BufferedReader getReader() throws IOException {
    34.         return new BufferedReader(new InputStreamReader(getInputStream()));
    35.     }
    36.     /**
    37.      * 首次获取流时,将流放入 缓存输入流 中
    38.      */
    39.     private void cacheInputStream() throws IOException {
    40.         // 缓存输入流以便多次读取。为了方便, 我使用 org.apache.commons IOUtils
    41.         cachedBytes = new ByteArrayOutputStream();
    42.         IOUtils.copy(super.getInputStream(), cachedBytes);
    43.     }
    44.     /**
    45.      * 读取缓存的请求正文的输入流
    46.      * 

    47.      * 用于根据 缓存输入流 创建一个可返回的
    48.      */
    49.     public static class CachedServletInputStream extends ServletInputStream {
    50.         private final ByteArrayInputStream input;
    51.         public CachedServletInputStream(byte[] buf) {
    52.             // 从缓存的请求正文创建一个新的输入流
    53.             input = new ByteArrayInputStream(buf);
    54.         }
    55.         @Override
    56.         public boolean isFinished() {
    57.             return false;
    58.         }
    59.         @Override
    60.         public boolean isReady() {
    61.             return false;
    62.         }
    63.         @Override
    64.         public void setReadListener(ReadListener listener) {
    65.         }
    66.         @Override
    67.         public int read() throws IOException {
    68.             return input.read();
    69.         }
    70.     }
    71. }

    HttpServletRequestInputStreamFilter

    1. package xyz.hlh.cryptotest.filter;
    2. import org.springframework.core.annotation.Order;
    3. import org.springframework.stereotype.Component;
    4. import xyz.hlh.cryptotest.utils.InputStreamHttpServletRequestWrapper;
    5. import javax.servlet.Filter;
    6. import javax.servlet.FilterChain;
    7. import javax.servlet.ServletException;
    8. import javax.servlet.ServletRequest;
    9. import javax.servlet.ServletResponse;
    10. import javax.servlet.http.HttpServletRequest;
    11. import java.io.IOException;
    12. import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
    13. /**
    14.  * @author HLH
    15.  * @description:
    16.  *      请求流转换为多次读取的请求流 过滤器
    17.  * @email 17703595860@163.com
    18.  * @date : Created in 2022/2/4 9:58
    19.  */
    20. @Component
    21. @Order(HIGHEST_PRECEDENCE + 1)  // 优先级最高
    22. public class HttpServletRequestInputStreamFilter implements Filter {
    23.     @Override
    24.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    25.         // 转换为可以多次获取流的request
    26.         HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    27.         InputStreamHttpServletRequestWrapper inputStreamHttpServletRequestWrapper = new InputStreamHttpServletRequestWrapper(httpServletRequest);
    28.         // 放行
    29.         chain.doFilter(inputStreamHttpServletRequestWrapper, response);
    30.     }
    31. }

    2.3 SpringBoot的参数校验validation

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

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

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

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

    ParamException

    1. package xyz.hlh.cryptotest.exception;
    2. import lombok.Getter;
    3. import java.util.List;
    4. /**
    5.  * @author HLH
    6.  * @description 自定义参数异常
    7.  * @email 17703595860@163.com
    8.  * @date Created in 2021/8/10 下午10:56
    9.  */
    10. @Getter
    11. public class ParamException extends Exception {
    12.     private final List<String> fieldList;
    13.     private final List<String> msgList;
    14.     public ParamException(List<String> fieldList, List<String> msgList) {
    15.         this.fieldList = fieldList;
    16.         this.msgList = msgList;
    17.     }
    18. }

    ValidationUtils

    1. package xyz.hlh.cryptotest.utils;
    2. import xyz.hlh.cryptotest.exception.CustomizeException;
    3. import xyz.hlh.cryptotest.exception.ParamException;
    4. import javax.validation.ConstraintViolation;
    5. import javax.validation.Validation;
    6. import javax.validation.Validator;
    7. import java.util.LinkedList;
    8. import java.util.List;
    9. import java.util.Set;
    10. /**
    11.  * @author HLH
    12.  * @description 验证工具类
    13.  * @email 17703595860@163.com
    14.  * @date Created in 2021/8/10 下午10:56
    15.  */
    16. public class ValidationUtils {
    17.     private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
    18.     /**
    19.      * 验证数据
    20.      * @param object 数据
    21.      */
    22.     public static void validate(Object object) throws CustomizeException {
    23.         Set<ConstraintViolation<Object>> validate = VALIDATOR.validate(object);
    24.         // 验证结果异常
    25.         throwParamException(validate);
    26.     }
    27.     /**
    28.      * 验证数据(分组)
    29.      * @param object 数据
    30.      * @param groups 所在组
    31.      */
    32.     public static void validate(Object objectClass<?> ... groups) throws CustomizeException {
    33.         Set<ConstraintViolation<Object>> validate = VALIDATOR.validate(object, groups);
    34.         // 验证结果异常
    35.         throwParamException(validate);
    36.     }
    37.     /**
    38.      * 验证数据中的某个字段(分组)
    39.      * @param object 数据
    40.      * @param propertyName 字段名称
    41.      */
    42.     public static void validate(Object objectString propertyName) throws CustomizeException {
    43.         Set<ConstraintViolation<Object>> validate = VALIDATOR.validateProperty(object, propertyName);
    44.         // 验证结果异常
    45.         throwParamException(validate);
    46.     }
    47.     /**
    48.      * 验证数据中的某个字段(分组)
    49.      * @param object 数据
    50.      * @param propertyName 字段名称
    51.      * @param groups 所在组
    52.      */
    53.     public static void validate(Object objectString propertyName, Class<?> ... groups) throws CustomizeException {
    54.         Set<ConstraintViolation<Object>> validate = VALIDATOR.validateProperty(object, propertyName, groups);
    55.         // 验证结果异常
    56.         throwParamException(validate);
    57.     }
    58.     /**
    59.      * 验证结果异常
    60.      * @param validate 验证结果
    61.      */
    62.     private static void throwParamException(Set<ConstraintViolation<Object>> validate) throws CustomizeException {
    63.         if (validate.size() > 0) {
    64.             List<String> fieldList = new LinkedList<>();
    65.             List<String> msgList = new LinkedList<>();
    66.             for (ConstraintViolation<Object> next : validate) {
    67.                 fieldList.add(next.getPropertyPath().toString());
    68.                 msgList.add(next.getMessage());
    69.             }
    70.             throw new ParamException(fieldList, msgList);
    71.         }
    72.     }
    73. }

    2.5 自定义starter

    自定义starter步骤

    • 创建工厂,编写功能代码

    • 声明自动配置类,把需要对外提供的对象创建好,通过配置类统一向外暴露

    • 在resource目录下准备一个名为spring/spring.factories的文件,以org.springframework.boot.autoconfigure.EnableAutoConfiguration为key,自动配置类为value列表,进行注册

    2.6 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需要的参数配置

    1. # 模式    cn.hutool.crypto.Mode
    2. crypto.mode=CTS
    3. # 补码方式 cn.hutool.crypto.Mode
    4. crypto.padding=PKCS5Padding
    5. # 秘钥
    6. crypto.key=testkey123456789
    7. # 盐
    8. crypto.iv=testiv1234567890

    spring.factories 自动配置文件

    1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    2. xyz.hlh.crypto.config.AppConfig

    CryptConfig AES需要的配置参数

    1. package xyz.hlh.crypto.config;
    2. import cn.hutool.crypto.Mode;
    3. import cn.hutool.crypto.Padding;
    4. import lombok.Data;
    5. import lombok.EqualsAndHashCode;
    6. import lombok.Getter;
    7. import org.springframework.boot.context.properties.ConfigurationProperties;
    8. import org.springframework.context.annotation.Configuration;
    9. import org.springframework.context.annotation.PropertySource;
    10. import java.io.Serializable;
    11. /**
    12.  * @author HLH
    13.  * @description: AES需要的配置参数
    14.  * @email 17703595860@163.com
    15.  * @date : Created in 2022/2/4 13:16
    16.  */
    17. @Configuration
    18. @ConfigurationProperties(prefix = "crypto")
    19. @PropertySource("classpath:crypto.properties")
    20. @Data
    21. @EqualsAndHashCode
    22. @Getter
    23. public class CryptConfig implements Serializable {
    24.     private Mode mode;
    25.     private Padding padding;
    26.     private String key;
    27.     private String iv;
    28. }

    AppConfig 自动配置类

    1. package xyz.hlh.crypto.config;
    2. import cn.hutool.crypto.symmetric.AES;
    3. import org.springframework.context.annotation.Bean;
    4. import org.springframework.context.annotation.Configuration;
    5. import javax.annotation.Resource;
    6. import java.nio.charset.StandardCharsets;
    7. /**
    8.  * @author HLH
    9.  * @description: 自动配置类
    10.  * @email 17703595860@163.com
    11.  * @date : Created in 2022/2/4 13:12
    12.  */
    13. @Configuration
    14. public class AppConfig {
    15.     @Resource
    16.     private CryptConfig cryptConfig;
    17.     @Bean
    18.     public AES aes() {
    19.         return new AES(cryptConfig.getMode(), cryptConfig.getPadding(), cryptConfig.getKey().getBytes(StandardCharsets.UTF_8), cryptConfig.getIv().getBytes(StandardCharsets.UTF_8));
    20.     }
    21. }

    DecryptRequestBodyAdvice 请求自动解密

    1. package xyz.hlh.crypto.advice;
    2. import com.fasterxml.jackson.databind.ObjectMapper;
    3. import lombok.SneakyThrows;
    4. import org.apache.commons.lang3.StringUtils;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.core.MethodParameter;
    7. import org.springframework.http.HttpInputMessage;
    8. import org.springframework.http.converter.HttpMessageConverter;
    9. import org.springframework.web.bind.annotation.ControllerAdvice;
    10. import org.springframework.web.context.request.RequestAttributes;
    11. import org.springframework.web.context.request.RequestContextHolder;
    12. import org.springframework.web.context.request.ServletRequestAttributes;
    13. import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
    14. import xyz.hlh.crypto.annotation.DecryptionAnnotation;
    15. import xyz.hlh.crypto.common.exception.ParamException;
    16. import xyz.hlh.crypto.constant.CryptoConstant;
    17. import xyz.hlh.crypto.entity.RequestBase;
    18. import xyz.hlh.crypto.entity.RequestData;
    19. import xyz.hlh.crypto.util.AESUtil;
    20. import javax.servlet.ServletInputStream;
    21. import javax.servlet.http.HttpServletRequest;
    22. import java.io.IOException;
    23. import java.lang.reflect.Type;
    24. /**
    25.  * @author HLH
    26.  * @description: requestBody 自动解密
    27.  * @email 17703595860@163.com
    28.  * @date : Created in 2022/2/4 15:12
    29.  */
    30. @ControllerAdvice
    31. public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
    32.     @Autowired
    33.     private ObjectMapper objectMapper;
    34.     /**
    35.      * 方法上有DecryptionAnnotation注解的,进入此拦截器
    36.      * @param methodParameter 方法参数对象
    37.      * @param targetType 参数的类型
    38.      * @param converterType 消息转换器
    39.      * @return true,进入,false,跳过
    40.      */
    41.     @Override
    42.     public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    43.         return methodParameter.hasMethodAnnotation(DecryptionAnnotation.class);
    44.     }
    45.     @Override
    46.     public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
    47.         return inputMessage;
    48.     }
    49.     /**
    50.      * 转换之后,执行此方法,解密,赋值
    51.      * @param body spring解析完的参数
    52.      * @param inputMessage 输入参数
    53.      * @param parameter 参数对象
    54.      * @param targetType 参数类型
    55.      * @param converterType 消息转换类型
    56.      * @return 真实的参数
    57.      */
    58.     @SneakyThrows
    59.     @Override
    60.     public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    61.         // 获取request
    62.         RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    63.         ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
    64.         if (servletRequestAttributes == null) {
    65.             throw new ParamException("request错误");
    66.         }
    67.         HttpServletRequest request = servletRequestAttributes.getRequest();
    68.         // 获取数据
    69.         ServletInputStream inputStream = request.getInputStream();
    70.         RequestData requestData = objectMapper.readValue(inputStream, RequestData.class);
    71.         if (requestData == null || StringUtils.isBlank(requestData.getText())) {
    72.             throw new ParamException("参数错误");
    73.         }
    74.         // 获取加密的数据
    75.         String text = requestData.getText();
    76.         // 放入解密之前的数据
    77.         request.setAttribute(CryptoConstant.INPUT_ORIGINAL_DATA, text);
    78.         // 解密
    79.         String decryptText = null;
    80.         try {
    81.             decryptText = AESUtil.decrypt(text);
    82.         } catch (Exception e) {
    83.             throw new ParamException("解密失败");
    84.         }
    85.         if (StringUtils.isBlank(decryptText)) {
    86.             throw new ParamException("解密失败");
    87.         }
    88.         // 放入解密之后的数据
    89.         request.setAttribute(CryptoConstant.INPUT_DECRYPT_DATA, decryptText);
    90.         // 获取结果
    91.         Object result = objectMapper.readValue(decryptText, body.getClass());
    92.         // 强制所有实体类必须继承RequestBase类,设置时间戳
    93.         if (result instanceof RequestBase) {
    94.             // 获取时间戳
    95.             Long currentTimeMillis = ((RequestBase) result).getCurrentTimeMillis();
    96.             // 有效期 60
    97.             long effective = 60*1000;
    98.             // 时间差
    99.             long expire = System.currentTimeMillis() - currentTimeMillis;
    100.             // 是否在有效期内
    101.             if (Math.abs(expire) > effective) {
    102.                 throw new ParamException("时间戳不合法");
    103.             }
    104.             // 返回解密之后的数据
    105.             return result;
    106.         } else {
    107.             throw new ParamException(String.format("请求参数类型:%s 未继承:%s", result.getClass().getName(), RequestBase.class.getName()));
    108.         }
    109.     }
    110.     /**
    111.      * 如果body为空,转为空对象
    112.      * @param body spring解析完的参数
    113.      * @param inputMessage 输入参数
    114.      * @param parameter 参数对象
    115.      * @param targetType 参数类型
    116.      * @param converterType 消息转换类型
    117.      * @return 真实的参数
    118.      */
    119.     @SneakyThrows
    120.     @Override
    121.     public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    122.         String typeName = targetType.getTypeName();
    123.         Class<?> bodyClass = Class.forName(typeName);
    124.         return bodyClass.newInstance();
    125.     }
    126. }

    EncryptResponseBodyAdvice 相应自动加密

    1. package xyz.hlh.crypto.advice;
    2. import cn.hutool.json.JSONUtil;
    3. import com.fasterxml.jackson.databind.ObjectMapper;
    4. import lombok.SneakyThrows;
    5. import org.apache.commons.lang3.StringUtils;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.core.MethodParameter;
    8. import org.springframework.http.MediaType;
    9. import org.springframework.http.ResponseEntity;
    10. import org.springframework.http.converter.HttpMessageConverter;
    11. import org.springframework.http.server.ServerHttpRequest;
    12. import org.springframework.http.server.ServerHttpResponse;
    13. import org.springframework.web.bind.annotation.ControllerAdvice;
    14. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    15. import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
    16. import xyz.hlh.crypto.annotation.EncryptionAnnotation;
    17. import xyz.hlh.crypto.common.entity.Result;
    18. import xyz.hlh.crypto.common.exception.CryptoException;
    19. import xyz.hlh.crypto.entity.RequestBase;
    20. import xyz.hlh.crypto.util.AESUtil;
    21. import java.lang.reflect.Type;
    22. /**
    23.  * @author HLH
    24.  * @description:
    25.  * @email 17703595860@163.com
    26.  * @date : Created in 2022/2/4 15:12
    27.  */
    28. @ControllerAdvice
    29. public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Result<?>> {
    30.     @Autowired
    31.     private ObjectMapper objectMapper;
    32.     @Override
    33.     public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    34.         ParameterizedTypeImpl genericParameterType = (ParameterizedTypeImpl)returnType.getGenericParameterType();
    35.         // 如果直接是Result,则返回
    36.         if (genericParameterType.getRawType() == Result.class && returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {
    37.             return true;
    38.         }
    39.         if (genericParameterType.getRawType() != ResponseEntity.class) {
    40.             return false;
    41.         }
    42.         // 如果是ResponseEntity<Result>
    43.         for (Type type : genericParameterType.getActualTypeArguments()) {
    44.             if (((ParameterizedTypeImpl) type).getRawType() == Result.class && returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {
    45.                 return true;
    46.             }
    47.         }
    48.         return false;
    49.     }
    50.     @SneakyThrows
    51.     @Override
    52.     public Result<?> beforeBodyWrite(Result<?> body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    53.         // 加密
    54.         Object data = body.getData();
    55.         // 如果data为空,直接返回
    56.         if (data == null) {
    57.             return body;
    58.         }
    59.         // 如果是实体,并且继承了Request,则放入时间戳
    60.         if (data instanceof RequestBase) {
    61.             ((RequestBase)data).setCurrentTimeMillis(System.currentTimeMillis());
    62.         }
    63.         String dataText = JSONUtil.toJsonStr(data);
    64.         // 如果data为空,直接返回
    65.         if (StringUtils.isBlank(dataText)) {
    66.             return body;
    67.         }
    68.         // 如果位数小于16,报错
    69.         if (dataText.length() < 16) {
    70.             throw new CryptoException("加密失败,数据小于16位");
    71.         }
    72.         String encryptText = AESUtil.encryptHex(dataText);
    73.         return Result.builder()
    74.                 .status(body.getStatus())
    75.                 .data(encryptText)
    76.                 .message(body.getMessage())
    77.                 .build();
    78.     }
    79. }

    5.4 crypto-test

    5.4.1 结构

    5.4.2 重要代码

    application.yml 配置文件

    1. spring:
    2.   mvc:
    3.     format:
    4.       date-time: yyyy-MM-dd HH:mm:ss
    5.       date: yyyy-MM-dd
    6.   # 日期格式化
    7.   jackson:
    8.     date-format: yyyy-MM-dd HH:mm:ss

    Teacher 实体类

    1. package xyz.hlh.crypto.entity;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.EqualsAndHashCode;
    5. import lombok.NoArgsConstructor;
    6. import org.hibernate.validator.constraints.Range;
    7. import javax.validation.constraints.NotBlank;
    8. import javax.validation.constraints.NotNull;
    9. import java.io.Serializable;
    10. import java.util.Date;
    11. /**
    12.  * @author HLH
    13.  * @description: Teacher实体类,使用SpringBoot的validation校验
    14.  * @email 17703595860@163.com
    15.  * @date : Created in 2022/2/4 10:21
    16.  */
    17. @Data
    18. @NoArgsConstructor
    19. @AllArgsConstructor
    20. @EqualsAndHashCode(callSuper = true)
    21. public class Teacher extends RequestBase implements Serializable {
    22.     @NotBlank(message = "姓名不能为空")
    23.     private String name;
    24.     @NotNull(message = "年龄不能为空")
    25.     @Range(min = 0, max = 150, message = "年龄不合法")
    26.     private Integer age;
    27.     @NotNull(message = "生日不能为空")
    28.     private Date birthday;
    29. }

    TestController 测试Controller

    1. package xyz.hlh.crypto.controller;
    2. import org.springframework.http.ResponseEntity;
    3. import org.springframework.validation.annotation.Validated;
    4. import org.springframework.web.bind.annotation.PostMapping;
    5. import org.springframework.web.bind.annotation.RequestBody;
    6. import org.springframework.web.bind.annotation.RestController;
    7. import xyz.hlh.crypto.annotation.DecryptionAnnotation;
    8. import xyz.hlh.crypto.annotation.EncryptionAnnotation;
    9. import xyz.hlh.crypto.common.entity.Result;
    10. import xyz.hlh.crypto.common.entity.ResultBuilder;
    11. import xyz.hlh.crypto.entity.Teacher;
    12. /**
    13.  * @author HLH
    14.  * @description: 测试Controller
    15.  * @email 17703595860@163.com
    16.  * @date : Created in 2022/2/4 9:16
    17.  */
    18. @RestController
    19. public class TestController implements ResultBuilder {
    20.     /**
    21.      * 直接返回对象,不加密
    22.      * @param teacher Teacher对象
    23.      * @return 不加密的对象
    24.      */
    25.     @PostMapping("/get")
    26.     public ResponseEntity<Result<?>> get(@Validated @RequestBody Teacher teacher) {
    27.         return success(teacher);
    28.     }
    29.     /**
    30.      * 返回加密后的数据
    31.      * @param teacher Teacher对象
    32.      * @return 返回加密后的数据 ResponseBody<Result>格式
    33.      */
    34.     @PostMapping("/encrypt")
    35.     @EncryptionAnnotation
    36.     public ResponseEntity<Result<?>> encrypt(@Validated @RequestBody Teacher teacher) {
    37.         return success(teacher);
    38.     }
    39.     /**
    40.      * 返回加密后的数据
    41.      * @param teacher Teacher对象
    42.      * @return 返回加密后的数据 Result格式
    43.      */
    44.     @PostMapping("/encrypt1")
    45.     @EncryptionAnnotation
    46.     public Result<?> encrypt1(@Validated @RequestBody Teacher teacher) {
    47.         return success(teacher).getBody();
    48.     }
    49.     /**
    50.      * 返回解密后的数据
    51.      * @param teacher Teacher对象
    52.      * @return 返回解密后的数据
    53.      */
    54.     @PostMapping("/decrypt")
    55.     @DecryptionAnnotation
    56.     public ResponseEntity<Result<?>> decrypt(@Validated @RequestBody Teacher teacher) {
    57.         return success(teacher);
    58.     }
    59. }
  • 相关阅读:
    mac搭建redis集群
    C++类与对象(下)
    初探JVM
    python opencv2滚轮快速切换图片
    【QT小记】QT中信号和槽的基本使用
    MySQL常见知识点(面试题)总结
    数字电路和模拟电路-9时序逻辑电路、计数器和寄存器
    初识EasyAR
    金仓数据库 KingbaseGIS 使用手册(8.11. 栅格处理函数)
    C++中cin输入空格
  • 原文地址:https://blog.csdn.net/m0_71777195/article/details/127960267