<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot中接口加密解密統(tǒng)一處理!

          共 37692字,需瀏覽 76分鐘

           ·

          2022-11-22 14:59

          大家好,我是寶哥

          1. 介紹

          在我們?nèi)粘5腏ava開發(fā)中,免不了和其他系統(tǒng)的業(yè)務(wù)交互,或者微服務(wù)之間的接口調(diào)用

          如果我們想保證數(shù)據(jù)傳輸?shù)陌踩瑢?duì)接口出參加密,入?yún)⒔饷堋?/p>

          但是不想寫重復(fù)代碼,我們可以提供一個(gè)通用starter,提供通用加密解密功能

          2. 前置知識(shí)

          2.1 hutool-crypto加密解密工具

          hutool-crypto提供了很多加密解密工具,包括對(duì)稱加密,非對(duì)稱加密,摘要加密等等,這不做詳細(xì)介紹。

          2.2 request流只能讀取一次的問題

          2.2.1 問題:

          在接口調(diào)用鏈中,request的請(qǐng)求流只能調(diào)用一次,處理之后,如果之后還需要用到請(qǐng)求流獲取數(shù)據(jù),就會(huì)發(fā)現(xiàn)數(shù)據(jù)為空。

          比如使用了filter或者aop在接口處理之前,獲取了request中的數(shù)據(jù),對(duì)參數(shù)進(jìn)行了校驗(yàn),那么之后就不能在獲取request請(qǐng)求流了

          2.2.2 解決辦法

          繼承HttpServletRequestWrapper,將請(qǐng)求中的流copy一份,復(fù)寫getInputStream和getReader方法供外部使用。每次調(diào)用后的getInputStream方法都是從復(fù)制出來的二進(jìn)制數(shù)組中進(jìn)行獲取,這個(gè)二進(jìn)制數(shù)組在對(duì)象存在期間一致存在。

          使用Filter過濾器,在一開始,替換request為自己定義的可以多次讀取流的request。

          這樣就實(shí)現(xiàn)了流的重復(fù)獲取

          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;

          /**
           * 請(qǐng)求流支持多次獲取
           */

          public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {

              /**
               * 用于緩存輸入流
               */

              private ByteArrayOutputStream cachedBytes;

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

              @Override
              public ServletInputStream getInputStream() throws IOException {
                  if (cachedBytes == null) {
                      // 首次獲取流時(shí),將流放入 緩存輸入流 中
                      cacheInputStream();
                  }

                  // 從 緩存輸入流 中獲取流并返回
                  return new CachedServletInputStream(cachedBytes.toByteArray());
              }

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

              /**
               * 首次獲取流時(shí),將流放入 緩存輸入流 中
               */

              private void cacheInputStream() throws IOException {
                  // 緩存輸入流以便多次讀取。為了方便, 我使用 org.apache.commons IOUtils
                  cachedBytes = new ByteArrayOutputStream();
                  IOUtils.copy(super.getInputStream(), cachedBytes);
              }

              /**
               * 讀取緩存的請(qǐng)求正文的輸入流
               * <p>
               * 用于根據(jù) 緩存輸入流 創(chuàng)建一個(gè)可返回的
               */

              public static class CachedServletInputStream extends ServletInputStream {

                  private final ByteArrayInputStream input;

                  public CachedServletInputStream(byte[] buf) {
                      // 從緩存的請(qǐng)求正文創(chuàng)建一個(gè)新的輸入流
                      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:
           *      請(qǐng)求流轉(zhuǎn)換為多次讀取的請(qǐng)求流 過濾器
           * @email [email protected]
           * @date : Created in 2022/2/4 9:58
           */

          @Component
          @Order(HIGHEST_PRECEDENCE + 1)  // 優(yōu)先級(jí)最高
          public class HttpServletRequestInputStreamFilter implements Filter {

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

                  // 轉(zhuǎn)換為可以多次獲取流的request
                  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
                  InputStreamHttpServletRequestWrapper inputStreamHttpServletRequestWrapper = new InputStreamHttpServletRequestWrapper(httpServletRequest);

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

          2.3 SpringBoot的參數(shù)校驗(yàn)validation

          為了減少接口中,業(yè)務(wù)代碼之前的大量冗余的參數(shù)校驗(yàn)代碼

          SpringBoot-validation提供了優(yōu)雅的參數(shù)校驗(yàn),入?yún)⒍际菍?shí)體類,在實(shí)體類字段上加上對(duì)應(yīng)注解,就可以在進(jìn)入方法之前,進(jìn)行參數(shù)校驗(yàn),如果參數(shù)錯(cuò)誤,會(huì)拋出錯(cuò)誤BindException,是不會(huì)進(jìn)入方法的。

          這種方法,必須要求在接口參數(shù)上加注解@Validated或者是@Valid

          但是很多清空下,我們希望在代碼中調(diào)用某個(gè)實(shí)體類的校驗(yàn)功能,所以需要如下工具類

          ParamException

          package xyz.hlh.cryptotest.exception;

          import lombok.Getter;

          import java.util.List;

          /**
           * @author HLH
           * @description 自定義參數(shù)異常
           * @email [email protected]
           * @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

          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 驗(yàn)證工具類
           * @email [email protected]
           * @date Created in 2021/8/10 下午10:56
           */

          public class ValidationUtils {

              private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();

              /**
               * 驗(yàn)證數(shù)據(jù)
               * @param object 數(shù)據(jù)
               */

              public static void validate(Object object) throws CustomizeException {

                  Set<ConstraintViolation<Object>> validate = VALIDATOR.validate(object);

                  // 驗(yàn)證結(jié)果異常
                  throwParamException(validate);
              }

              /**
               * 驗(yàn)證數(shù)據(jù)(分組)
               * @param object 數(shù)據(jù)
               * @param groups 所在組
               */

              public static void validate(Object object, Class<?> ... groups) throws CustomizeException {

                  Set<ConstraintViolation<Object>> validate = VALIDATOR.validate(object, groups);

                  // 驗(yàn)證結(jié)果異常
                  throwParamException(validate);
              }

              /**
               * 驗(yàn)證數(shù)據(jù)中的某個(gè)字段(分組)
               * @param object 數(shù)據(jù)
               * @param propertyName 字段名稱
               */

              public static void validate(Object object, String propertyName) throws CustomizeException {
                  Set<ConstraintViolation<Object>> validate = VALIDATOR.validateProperty(object, propertyName);

                  // 驗(yàn)證結(jié)果異常
                  throwParamException(validate);

              }

              /**
               * 驗(yàn)證數(shù)據(jù)中的某個(gè)字段(分組)
               * @param object 數(shù)據(jù)
               * @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);

                  // 驗(yàn)證結(jié)果異常
                  throwParamException(validate);

              }

              /**
               * 驗(yàn)證結(jié)果異常
               * @param validate 驗(yàn)證結(jié)果
               */

              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.5 自定義starter

          自定義starter步驟

          • 創(chuàng)建工廠,編寫功能代碼
          • 聲明自動(dòng)配置類,把需要對(duì)外提供的對(duì)象創(chuàng)建好,通過配置類統(tǒng)一向外暴露
          • 在resource目錄下準(zhǔn)備一個(gè)名為spring/spring.factories的文件,以org.springframework.boot.autoconfigure.EnableAutoConfiguration為key,自動(dòng)配置類為value列表,進(jìn)行注冊(cè)

          2.6 RequestBodyAdvice和ResponseBodyAdvice

          • RequestBodyAdvice是對(duì)請(qǐng)求的json串進(jìn)行處理, 一般使用環(huán)境是處理接口參數(shù)的自動(dòng)解密
          • ResponseBodyAdvice是對(duì)請(qǐng)求相應(yīng)的jsoin傳進(jìn)行處理,一般用于相應(yīng)結(jié)果的加密

          3. 功能介紹

          接口相應(yīng)數(shù)據(jù)的時(shí)候,返回的是加密之后的數(shù)據(jù) 接口入?yún)⒌臅r(shí)候,接收的是解密之后的數(shù)據(jù),但是在進(jìn)入接口之前,會(huì)自動(dòng)解密,取得對(duì)應(yīng)的數(shù)據(jù)

          4. 功能細(xì)節(jié)

          加密解密使用對(duì)稱加密的AES算法,使用hutool-crypto模塊進(jìn)行實(shí)現(xiàn)

          所有的實(shí)體類提取一個(gè)公共父類,包含屬性時(shí)間戳,用于加密數(shù)據(jù)返回之后的實(shí)效性,如果超過60分鐘,那么其他接口將不進(jìn)行處理。

          如果接口加了加密注解EncryptionAnnotation,并且返回統(tǒng)一的json數(shù)據(jù)Result類,則自動(dòng)對(duì)數(shù)據(jù)進(jìn)行加密。如果是繼承了統(tǒng)一父類RequestBase的數(shù)據(jù),自動(dòng)注入時(shí)間戳,確保數(shù)據(jù)的時(shí)效性

          如果接口加了解密注解DecryptionAnnotation,并且參數(shù)使用RequestBody注解標(biāo)注,傳入json使用統(tǒng)一格式RequestData類,并且內(nèi)容是繼承了包含時(shí)間長(zhǎng)的父類RequestBase,則自動(dòng)解密,并且轉(zhuǎn)為對(duì)應(yīng)的數(shù)據(jù)類型

          功能提供Springboot的starter,實(shí)現(xiàn)開箱即用

          5. 代碼實(shí)現(xiàn)

          https://gitee.com/springboot-hlh/spring-boot-csdn/tree/master/09-spring-boot-interface-crypto

          5.1 項(xiàng)目結(jié)構(gòu)

          5.2 crypto-common

          5.2.1 結(jié)構(gòu)

          5.3 crypto-spring-boot-starter

          5.3.1 接口
          5.3.2 重要代碼

          crypto.properties AES需要的參數(shù)配置

          # 模式    cn.hutool.crypto.Mode
          crypto.mode=CTS
          # 補(bǔ)碼方式 cn.hutool.crypto.Mode
          crypto.padding=PKCS5Padding
          # 秘鑰
          crypto.key=testkey123456789
          # 鹽
          crypto.iv=testiv1234567890

          spring.factories 自動(dòng)配置文件

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

          CryptConfig AES需要的配置參數(shù)

          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需要的配置參數(shù)
           * @email [email protected]
           * @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 自動(dòng)配置類

          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: 自動(dòng)配置類
           * @email [email protected]
           * @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 請(qǐng)求自動(dòng)解密

          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 自動(dòng)解密
           * @email [email protected]
           * @date : Created in 2022/2/4 15:12
           */

          @ControllerAdvice
          public class DecryptRequestBodyAdvice implements RequestBodyAdvice {

              @Autowired
              private ObjectMapper objectMapper;

              /**
               * 方法上有DecryptionAnnotation注解的,進(jìn)入此攔截器
               * @param methodParameter 方法參數(shù)對(duì)象
               * @param targetType 參數(shù)的類型
               * @param converterType 消息轉(zhuǎn)換器
               * @return true,進(jìn)入,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;
              }

              /**
               * 轉(zhuǎn)換之后,執(zhí)行此方法,解密,賦值
               * @param body spring解析完的參數(shù)
               * @param inputMessage 輸入?yún)?shù)
               * @param parameter 參數(shù)對(duì)象
               * @param targetType 參數(shù)類型
               * @param converterType 消息轉(zhuǎn)換類型
               * @return 真實(shí)的參數(shù)
               */

              @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錯(cuò)誤");
                  }

                  HttpServletRequest request = servletRequestAttributes.getRequest();

                  // 獲取數(shù)據(jù)
                  ServletInputStream inputStream = request.getInputStream();
                  RequestData requestData = objectMapper.readValue(inputStream, RequestData.class);

                  if (requestData == null || StringUtils.isBlank(requestData.getText())) {
                      throw new ParamException("參數(shù)錯(cuò)誤");
                  }

                  // 獲取加密的數(shù)據(jù)
                  String text = requestData.getText();

                  // 放入解密之前的數(shù)據(jù)
                  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("解密失敗");
                  }

                  // 放入解密之后的數(shù)據(jù)
                  request.setAttribute(CryptoConstant.INPUT_DECRYPT_DATA, decryptText);

                  // 獲取結(jié)果
                  Object result = objectMapper.readValue(decryptText, body.getClass());

                  // 強(qiáng)制所有實(shí)體類必須繼承RequestBase類,設(shè)置時(shí)間戳
                  if (result instanceof RequestBase) {
                      // 獲取時(shí)間戳
                      Long currentTimeMillis = ((RequestBase) result).getCurrentTimeMillis();
                      // 有效期 60秒
                      long effective = 60*1000;

                      // 時(shí)間差
                      long expire = System.currentTimeMillis() - currentTimeMillis;

                      // 是否在有效期內(nèi)
                      if (Math.abs(expire) > effective) {
                          throw new ParamException("時(shí)間戳不合法");
                      }

                      // 返回解密之后的數(shù)據(jù)
                      return result;
                  } else {
                      throw new ParamException(String.format("請(qǐng)求參數(shù)類型:%s 未繼承:%s", result.getClass().getName(), RequestBase.class.getName()));
                  }
              }

              /**
               * 如果body為空,轉(zhuǎn)為空對(duì)象
               * @param body spring解析完的參數(shù)
               * @param inputMessage 輸入?yún)?shù)
               * @param parameter 參數(shù)對(duì)象
               * @param targetType 參數(shù)類型
               * @param converterType 消息轉(zhuǎn)換類型
               * @return 真實(shí)的參數(shù)
               */

              @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 相應(yīng)自動(dòng)加密

          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 [email protected]
           * @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;
                  }

                  // 如果是實(shí)體,并且繼承了Request,則放入時(shí)間戳
                  if (data instanceof RequestBase) {
                      ((RequestBase)data).setCurrentTimeMillis(System.currentTimeMillis());
                  }

                  String dataText = JSONUtil.toJsonStr(data);

                  // 如果data為空,直接返回
                  if (StringUtils.isBlank(dataText)) {
                      return body;
                  }

                  // 如果位數(shù)小于16,報(bào)錯(cuò)
                  if (dataText.length() < 16) {
                      throw new CryptoException("加密失敗,數(shù)據(jù)小于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 結(jié)構(gòu)
          5.4.2 重要代碼

          application.yml 配置文件

          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 實(shí)體類

          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實(shí)體類,使用SpringBoot的validation校驗(yàn)
           * @email [email protected]
           * @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 測(cè)試Controller

          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: 測(cè)試Controller
           * @email [email protected]
           * @date : Created in 2022/2/4 9:16
           */

          @RestController
          public class TestController implements ResultBuilder {

              /**
               * 直接返回對(duì)象,不加密
               * @param teacher Teacher對(duì)象
               * @return 不加密的對(duì)象
               */

              @PostMapping("/get")
              public ResponseEntity<Result<?>> get(@Validated @RequestBody Teacher teacher) {
                  return success(teacher);
              }

              /**
               * 返回加密后的數(shù)據(jù)
               * @param teacher Teacher對(duì)象
               * @return 返回加密后的數(shù)據(jù) ResponseBody<Result>格式
               */

              @PostMapping("/encrypt")
              @EncryptionAnnotation
              public ResponseEntity<Result<?>> encrypt(@Validated @RequestBody Teacher teacher) {
                  return success(teacher);
              }

              /**
               * 返回加密后的數(shù)據(jù)
               * @param teacher Teacher對(duì)象
               * @return 返回加密后的數(shù)據(jù) Result格式
               */

              @PostMapping("/encrypt1")
              @EncryptionAnnotation
              public Result<?> encrypt1(@Validated @RequestBody Teacher teacher) {
                  return success(teacher).getBody();
              }

              /**
               * 返回解密后的數(shù)據(jù)
               * @param teacher Teacher對(duì)象
               * @return 返回解密后的數(shù)據(jù)
               */

              @PostMapping("/decrypt")
              @DecryptionAnnotation
              public ResponseEntity<Result<?>> decrypt(@Validated @RequestBody Teacher teacher) {
                  return success(teacher);
              }

          }

          來源:blog.csdn.net/HLH_2021/article/details/122785888

          精彩推薦:

          這是我見過最好的流程引擎架構(gòu)設(shè)計(jì)!

          SpringBoot+@Async注解一起用,速度提升100倍!

          11 款超贊的 MySQL 圖形化工具,好用!

          阿里內(nèi)部這套超全面的MySQL調(diào)優(yōu)絕密方案太好用了,沒有之一!!!公司一直用。。。

          基于 Spring Boot + Vue 實(shí)現(xiàn)的可視化拖拽編輯的大屏項(xiàng)目

          網(wǎng)關(guān)系統(tǒng)就該這么設(shè)計(jì)(萬能通用),穩(wěn)的一批!


          瀏覽 71
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情五月图片亚洲婷婷 | 色情 视频网站入口第一集 | 欧美视频日本麻豆 | 成人网站视频 | 苍井空精毛片精品久久久 |