<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 接口加密解密,新姿勢!

          共 38448字,需瀏覽 77分鐘

           ·

          2022-12-17 13:35

          來源:網絡

          • 1.介紹
          • 2.前置知識
            • 2.1 hutool-crypto加密解密工具
            • 2.2 request流只能讀取一次的問題
            • 2.3 SpringBoot的參數校驗validation
            • 2.4 自定義starter
            • 2.5 RequestBodyAdvice和ResponseBodyAdvice
          • 3.功能介紹
          • 4.功能細節(jié)
          • 5.代碼實現
            • 5.1 項目結構
            • 5.2 crypto-common
            • 5.3 crypto-spring-boot-starter
            • 5.4 crypto-test

          1. 介紹

          在我們日常的Java開發(fā)中,免不了和其他系統(tǒng)的業(yè)務交互,或者微服務之間的接口調用

          如果我們想保證數據傳輸的安全,對接口出參加密,入參解密。

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

          2. 前置知識

          2.1 hutool-crypto加密解密工具

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

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

          2.2.1 問題:

          在接口調用鏈中,request的請求流只能調用一次,處理之后,如果之后還需要用到請求流獲取數據,就會發(fā)現數據為空。

          比如使用了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>
               * 用于根據 緩存輸入流 創(chuàng)建一個可返回的
               */
              public static class CachedServletInputStream extends ServletInputStream {

                  private final ByteArrayInputStream input;

                  public CachedServletInputStream(byte[] buf) {
                      // 從緩存的請求正文創(chuàng)建一個新的輸入流
                      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 [email protected]
           * @date : Created in 2022/2/4 9:58
           */
          @Component
          @Order(HIGHEST_PRECEDENCE + 1)  // 優(yōu)先級最高
          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

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

          SpringBoot-validation提供了優(yōu)雅的參數校驗,入參都是實體類,在實體類字段上加上對應注解,就可以在進入方法之前,進行參數校驗,如果參數錯誤,會拋出錯誤BindException,是不會進入方法的。

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

          但是很多清空下,我們希望在代碼中調用某個實體類的校驗功能,所以需要如下工具類

          ParamException
          package xyz.hlh.cryptotest.exception;

          import lombok.Getter;

          import java.util.List;

          /**
           * @author HLH
           * @description 自定義參數異常
           * @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 驗證工具類
           * @email [email protected]
           * @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步驟

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

          2.5 RequestBodyAdvice和ResponseBodyAdvice

          • RequestBodyAdvice是對請求的json串進行處理, 一般使用環(huán)境是處理接口參數的自動解密
          • ResponseBodyAdvice是對請求相應的jsoin傳進行處理,一般用于相應結果的加密

          3. 功能介紹

          接口相應數據的時候,返回的是加密之后的數據 接口入參的時候,接收的是解密之后的數據,但是在進入接口之前,會自動解密,取得對應的數據

          4. 功能細節(jié)

          加密解密使用對稱加密的AES算法,使用hutool-crypto模塊進行實現

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

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

          如果接口加了解密注解DecryptionAnnotation,并且參數使用RequestBody注解標注,傳入json使用統(tǒng)一格式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需要的參數配置

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

          spring.factories 自動配置文件

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

          CryptConfig AES需要的配置參數

          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 [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 自動配置類

          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 [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 請求自動解密

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

              /**
               * 轉換之后,執(zhí)行此方法,解密,賦值
               * @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 相應自動加密

          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;
                  }

                  // 如果是實體,并且繼承了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 配置文件

          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 實體類

          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 [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 測試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: 測試Controller
           * @email [email protected]
           * @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);
              }

          }

          推薦閱讀:

          世界的真實格局分析,地球人類社會底層運行原理

          不是你需要中臺,而是一名合格的架構師(附各大廠中臺建設PPT)

          企業(yè)IT技術架構規(guī)劃方案

          論數字化轉型——轉什么,如何轉?

          華為干部與人才發(fā)展手冊(附PPT)

          企業(yè)10大管理流程圖,數字化轉型從業(yè)者必備!

          【中臺實踐】華為大數據中臺架構分享.pdf

          華為的數字化轉型方法論

          華為如何實施數字化轉型(附PPT)

          超詳細280頁Docker實戰(zhàn)文檔!開放下載

          華為大數據解決方案(PPT)

          瀏覽 69
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  国产日视频 | 网站黄片视频 | 成人AV导航 | 免费在线黄片 | 亚洲操逼图 |