最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用注解+RequestBodyAdvice實現(xiàn)http請求內容加解密方式

 更新時間:2021年06月29日 10:22:27   作者:more_try  
這篇文章主要介紹了使用注解+RequestBodyAdvice實現(xiàn)http請求內容加解密方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

注解主要用來指定那些需要加解密的controller方法

實現(xiàn)比較簡單

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SecretAnnotation {
    boolean encode() default false;
    boolean decode() default false;
}

使用時添加注解在controller的方法上

    @PostMapping("/preview")
    @SecretAnnotation(decode = true)
    public ResponseVO<ContractSignVO> previewContract(@RequestBody FillContractDTO fillContractDTO)  {
        return contractSignService.previewContract(fillContractDTO);
    }

請求數(shù)據(jù)由二進制流轉為類對象數(shù)據(jù),對于加密過的數(shù)據(jù),需要在二進制流被處理之前進行解密,否則在轉為類對象時會因為數(shù)據(jù)格式不匹配而報錯。

因此使用RequestBodyAdvice的beforeBodyRead方法來處理。

@Slf4j
@RestControllerAdvice
public class MyRequestControllerAdvice implements RequestBodyAdvice {
    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return methodParameter.hasParameterAnnotation(RequestBody.class);
    }
    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }
    @Autowired
    private MySecretUtil mySecretUtil;
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        if (methodParameter.getMethod().isAnnotationPresent(SecretAnnotation.class)) {
            SecretAnnotation secretAnnotation = methodParameter.getMethod().getAnnotation(SecretAnnotation.class);
            if (secretAnnotation.decode()) {
                return new HttpInputMessage() {
                    @Override
                    public InputStream getBody() throws IOException {
                        List<String> appIdList = httpInputMessage.getHeaders().get("appId");
                        if (appIdList.isEmpty()){
                            throw new RuntimeException("請求頭缺少appID");
                        }
                        String appId = appIdList.get(0);
                        String bodyStr = IOUtils.toString(httpInputMessage.getBody(),"utf-8");
                        bodyStr = mySecretUtil.decode(bodyStr,appId);
                        return  IOUtils.toInputStream(bodyStr,"utf-8");
                    }
                    @Override
                    public HttpHeaders getHeaders() {
                        return httpInputMessage.getHeaders();
                    }
                };
            }
        }
        return httpInputMessage;
    }
    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }
}

mySecretUtil.decode(bodyStr,appId)的內容是,通過請求頭中的AppID去數(shù)據(jù)庫中查找對于的秘鑰,之后進行解密,返回解密后的字符串。

再通過common.io包中提供的工具類IOUtils將字符串轉為inputstream流,替換HttpInputMessage,返回一個body數(shù)據(jù)為解密后的二進制流的HttpInputMessage。

Stringboot RequestBodyAdvice接口如何實現(xiàn)請求響應加解密

在實際項目中,我們常常需要在請求前后進行一些操作,比如:參數(shù)解密/返回結果加密,打印請求參數(shù)和返回結果的日志等。這些與業(yè)務無關的東西,我們不希望寫在controller方法中,造成代碼重復可讀性變差。這里,我們講講使用@ControllerAdvice和RequestBodyAdvice、ResponseBodyAdvice來對請求前后進行處理(本質上就是AOP),來實現(xiàn)日志記錄每一個請求的參數(shù)和返回結果。

1.加解密工具類

package com.linkus.common.utils;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import javax.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Aes {
    /**
     *
     * @author ngh
     * AES128 算法
     *
     * CBC 模式
     *
     * PKCS7Padding 填充模式
     *
     * CBC模式需要添加偏移量參數(shù)iv,必須16位
     * 密鑰 sessionKey,必須16位
     *
     * 介于java 不支持PKCS7Padding,只支持PKCS5Padding 但是PKCS7Padding 和 PKCS5Padding 沒有什么區(qū)別
     * 要實現(xiàn)在java端用PKCS7Padding填充,需要用到bouncycastle組件來實現(xiàn)
     */
    private String sessionKey="加解密密鑰";
    // 偏移量 16位
    private static  String iv="偏移量";
    // 算法名稱
    final String KEY_ALGORITHM = "AES";
    // 加解密算法/模式/填充方式
    final String algorithmStr = "AES/CBC/PKCS7Padding";
    // 加解密 密鑰 16位
    byte[] ivByte;
    byte[] keybytes;
    private Key key;
    private Cipher cipher;
    boolean isInited = false;
    public void init() {
        // 如果密鑰不足16位,那么就補足.  這個if 中的內容很重要
        keybytes = iv.getBytes();
        ivByte = iv.getBytes();
        Security.addProvider(new BouncyCastleProvider());
        // 轉化成JAVA的密鑰格式
        key = new SecretKeySpec(keybytes, KEY_ALGORITHM);
        try {
            // 初始化cipher
            cipher = Cipher.getInstance(algorithmStr, "BC");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    /**
     * 加密方法
     *
     * @param content
     *            要加密的字符串
     *            加密密鑰
     * @return
     */
    public String encrypt(String content) {
        byte[] encryptedText = null;
        byte[] contentByte = content.getBytes();
        init();
        try {
            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivByte));
            encryptedText = cipher.doFinal(contentByte);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return new String(Hex.encode(encryptedText));
    }
    /**
     * 解密方法
     *
     * @param encryptedData
     *            要解密的字符串
     *            解密密鑰
     * @return
     */
    public String decrypt(String encryptedData) {
        byte[] encryptedText = null;
        byte[] encryptedDataByte = Hex.decode(encryptedData);
        init();
        try {
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivByte));
            encryptedText = cipher.doFinal(encryptedDataByte);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return new String(encryptedText);
    }
    public static void main(String[] args) {
        Aes aes = new Aes();
        String a="{\n" +
                "\"distance\":\"1000\",\n" +
                "\"longitude\":\"28.206471\",\n" +
                "\"latitude\":\"112.941301\"\n" +
                "}";
        //加密字符串
        //String content = "孟飛快跑";
        // System.out.println("加密前的:" + content);
//        System.out.println("加密密鑰:" + new String(keybytes));
        // 加密方法
        String enc = aes.encrypt(a);
        System.out.println("加密后的內容:" + enc);
        String dec="";
        // 解密方法
        try {
            dec = aes.decrypt(enc);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("解密后的內容:" + dec);
    }
}

2.請求解密

前端頁面?zhèn)鬟^來的是密文,我們需要在Controller獲取請求之前對密文解密然后傳給Controller

package com.linkus.common.filter;
import com.alibaba.fastjson.JSON;
import com.linkus.common.constant.KPlatResponseCode;
import com.linkus.common.exception.CustomException;
import com.linkus.common.exception.JTransException;
import com.linkus.common.service.util.MyHttpInputMessage;
import com.linkus.common.utils.Aes;
import com.linkus.common.utils.http.HttpHelper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.Type;
/**
 * 請求參數(shù) 解密操作
 *
 * @Author: Java碎碎念
 * @Date: 2019/10/24 21:31
 *
 */
@Component
//可以配置指定需要解密的包,支持多個
@ControllerAdvice(basePackages = {"com.linkus.project"})
@Slf4j
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
    Logger log = LoggerFactory.getLogger(getClass());
    Aes aes=new Aes();
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    	//true開啟功能,false關閉這個功能
        return true;
    }
//在讀取請求之前做處理
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> selectedConverterType) throws IOException {
        //獲取請求數(shù)據(jù)
        String string = "";
        BufferedReader bufferedReader = null;
        InputStream inputStream = inputMessage.getBody();
            //這個request其實就是入?yún)?可以從這里獲取流
            //入?yún)⒎旁贖ttpInputMessage里面  這個方法的返回值也是HttpInputMessage
            try {
            string=getRequestBodyStr(inputStream,bufferedReader);
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }
        /*****************進行解密start*******************/
        String decode = null;
        if(HttpHelper.isEncrypted(inputMessage.getHeaders())){
        try {
            //            //解密操作
            //Map<String,String> dataMap = (Map)body;
            //log.info("接收到原始請求數(shù)據(jù)={}", string);
            // inputData 為待加解密的數(shù)據(jù)源
			//解密
            decode= aes.decrypt(string);
            //log.info("解密后數(shù)據(jù)={}",decode);
        } catch (Exception e ) {
            log.error("加解密錯誤:",e);
           throw  new CustomException(KPlatResponseCode.MSG_DECRYPT_TIMEOUT,KPlatResponseCode.CD_DECRYPT_TIMEOUT);
        }
        //把數(shù)據(jù)放到我們封裝的對象中
        }else{
            decode = string;
        }
       // log.info("接收到請求數(shù)據(jù)={}", decode);
//        log.info("接口請求地址{}",((HttpServletRequest)inputMessage).getRequestURI());
        return new MyHttpInputMessage(inputMessage.getHeaders(), new ByteArrayInputStream(decode.getBytes("UTF-8")));
    }
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }
    @Override
    public Object handleEmptyBody(@Nullable Object var1, HttpInputMessage var2, MethodParameter var3, Type var4, Class<? extends HttpMessageConverter<?>> var5) {
        return var1;
    }
	//自己寫的方法,不是接口的方法,處理密文
    public String getRequestBodyStr( InputStream inputStream,BufferedReader bufferedReader) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        String string = stringBuilder.toString();
        return string;
    }
}

3.響應加密

將返給前端的響應加密,保證數(shù)據(jù)的安全性

package com.linkus.common.filter;
import com.alibaba.fastjson.JSON;
import com.linkus.common.utils.Aes;
import com.linkus.common.utils.DesUtil;
import com.linkus.common.utils.http.HttpHelper;
import io.swagger.models.auth.In;
import lombok.experimental.Helper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * 請求參數(shù) 加密操作
 *
 * @Author: Java碎碎念
 * @Date: 2019/10/24 21:31
 *
 */
@Component
@ControllerAdvice(basePackages = {"com.linkus.project"})
@Slf4j
public class EncryResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    Logger log = LoggerFactory.getLogger(getClass());
    Aes aes=new Aes();
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }
    
    @Override
    public Object beforeBodyWrite(Object obj, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        String returnStr = "";
        Object retObj = null;
        log.info("接口請求地址{}",serverHttpRequest.getURI());
        //日志過濾
        //retObj=infofilter.getInfoFilter(returnType,obj);
        if(HttpHelper.isEncrypted(serverHttpRequest.getHeaders())) {
            try {
                //添加encry header,告訴前端數(shù)據(jù)已加密
                //serverHttpResponse.getHeaders().add("infoe", "e=a");
                //獲取請求數(shù)據(jù)
                String srcData = JSON.toJSONString(obj);
                //加密
                returnStr = aes.encrypt(srcData).replace("\r\n", "");
                //log.info("原始數(shù)據(jù)={},加密后數(shù)據(jù)={}", obj, returnStr);
                return returnStr;
            } catch (Exception e) {
                log.error("異常!", e);
            }
        }
        log.info("原始數(shù)據(jù)={}",JSON.toJSONString(obj));
        return obj;
    }
}

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • @Bean注解和@Configuration、@Component注解組合使用的區(qū)別

    @Bean注解和@Configuration、@Component注解組合使用的區(qū)別

    這篇文章主要介紹了@Bean注解和@Configuration、@Component注解組合使用的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 使用純Java實現(xiàn)一個WebSSH項目的示例代碼

    使用純Java實現(xiàn)一個WebSSH項目的示例代碼

    這篇文章主要介紹了使用純Java實現(xiàn)一個WebSSH項目,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-03-03
  • Spring boot 集成 Druid 數(shù)據(jù)源過程詳解

    Spring boot 集成 Druid 數(shù)據(jù)源過程詳解

    這篇文章主要介紹了Spring boot 集成 Druid 數(shù)據(jù)源過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-08-08
  • JUC之Semaphore源碼分析

    JUC之Semaphore源碼分析

    這篇文章主要為大家詳細分析了JUC之Semaphore源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • java身份證驗證代碼實現(xiàn)

    java身份證驗證代碼實現(xiàn)

    java身份證驗證代碼實現(xiàn),需要的朋友可以參考一下
    2013-02-02
  • Java?注解@PostConstruct的原理及最佳使用場景分析

    Java?注解@PostConstruct的原理及最佳使用場景分析

    @PostConstruct?是?Java?中非常實用的注解,尤其是在?Spring?等框架中,它使得開發(fā)者可以方便地在?Bean?初始化后執(zhí)行額外的操作,本文給大家介紹@PostConstruct?的原理、使用場景及最佳實踐,感興趣的朋友一起看看吧
    2025-04-04
  • Java實現(xiàn)上傳和下載功能(支持多個文件同時上傳)

    Java實現(xiàn)上傳和下載功能(支持多個文件同時上傳)

    這篇文章主要介紹了Java實現(xiàn)上傳和下載功能,支持多個文件同時上傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • java實現(xiàn)根據(jù)ip地址獲取地理位置

    java實現(xiàn)根據(jù)ip地址獲取地理位置

    本文給大家匯總介紹了2種分別使用新浪和淘寶接口,實現(xiàn)根據(jù)IP地址獲取詳細的地理位置的代碼,非常的實用,有需要的小伙伴可以參考下。
    2016-03-03
  • Java實現(xiàn)桌面日歷

    Java實現(xiàn)桌面日歷

    這篇文章主要為大家詳細介紹了Java實現(xiàn)桌面日歷,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • java設計模式筆記之代理模式

    java設計模式筆記之代理模式

    這篇文章主要為大家詳細介紹了java設計模式之代理模式筆記,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10

最新評論

龙口市| 永胜县| 博野县| 江达县| 荔浦县| 莱西市| 乌恰县| 光山县| 新乐市| 靖江市| 蒲江县| 贵州省| 洛川县| 洮南市| 海阳市| 资溪县| 城步| 建阳市| 杂多县| 景东| 五原县| 肥乡县| 瑞安市| 兴宁市| 巴中市| 通许县| 丹寨县| 铜陵市| 虹口区| 鸡西市| 铁岭市| 精河县| 阿克陶县| 巴彦淖尔市| 进贤县| 筠连县| 岳阳市| 抚松县| 双桥区| 卫辉市| 香河县|