菜鸟笔记
提升您的技术认知

接口入参与返回数据aes加解密-ag真人游戏

前言

  • 出于项目安全方面的考虑,对接口的入参和返回数据进行加解密,综合考虑效率和安全性总重采用aes的对称加密方式(前后台数据传输时采用base64编码否则会出现乱码现象)废话不多说 直接上代码

请求的拦截器类

注意这里面的aesrequestwrapper类,这个类重写获取流的方法 因为request中的流只能被读取一次,在解密的时候被你读取的话再往应用层传就会报错

import org.springframework.stereotype.component;
import javax.servlet.*;
import javax.servlet.http.httpservletrequest;
import java.io.ioexception;
/**
 * @description base64的转换拦截器类 拦截后台的请求进行数据的加解密操作
 * @author liang
 */
@component
public class aesfilter implements filter {
  
    @override
    public void init(filterconfig filterconfig) throws servletexception {
  
    }
    @override
    public void dofilter(servletrequest request, servletresponse response, filterchain chain) throws ioexception, servletexception {
  
        aesrequestwrapper requestwrapper = null;
        if (request instanceof httpservletrequest) {
  
            //把从request读到的流放入body字符串中(做了个base64的转换)
            // 在重写request的getinputstream中的read方法  每次读取request的流实际相当于读取body字符串的byte
            requestwrapper = new aesrequestwrapper((httpservletrequest) request);
        }
        if (requestwrapper != null ){
  
            // 在chain.dofiler方法中传递新的request对象
            chain.dofilter(requestwrapper, response);
        }else {
  
            chain.dofilter(request, response);
        }
    }
    @override
    public void destroy() {
  
    }
}

aesrequestwrapper重写读取流的类

注意:这里的base64是jdk1.8的类,他与之前老的base64加解密可能会出现不兼容的问题(让前端也用与jdk1.8匹配的base64加解密方式) aescoder是我自己写的aes加解密类

import org.slf4j.logger;
import org.slf4j.loggerfactory;
import javax.servlet.readlistener;
import javax.servlet.servletinputstream;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletrequestwrapper;
import java.io.*;
import java.util.base64;
/**
 * @author liang
 */
public class aesrequestwrapper extends httpservletrequestwrapper {
  
    private static final logger logger = loggerfactory.getlogger(aesrequestwrapper.class);
    /**
     * 存储请求数据
     */
    private string body;
    public aesrequestwrapper(httpservletrequest request) {
  
        super(request);
        renewbody(request);
    }
    /**
     * 重写getinputstream方法
     */
    @override
    public servletinputstream getinputstream() {
  
        final bytearrayinputstream bytearrayinputstream = new bytearrayinputstream(body.getbytes());
        return new servletinputstream() {
  
            @override
            public boolean isfinished() {
  
                return false;
            }
            @override
            public boolean isready() {
  
                return false;
            }
            @override
            public void setreadlistener(readlistener readlistener) {
  
            }
            @override
            public int read() {
  
                return bytearrayinputstream.read();
            }
        };
    }
    /**
     * 重写getreader方法
     */
    @override
    public bufferedreader getreader() {
  
        return new bufferedreader(new inputstreamreader(this.getinputstream()));
    }
    /**
     * 读取body的值
     */
    private void renewbody(httpservletrequest request) {
  
        stringbuilder stringbuilder = new stringbuilder();
        bufferedreader bufferedreader = null;
        try {
  
            inputstream inputstream = request.getinputstream();
            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);
                }
            }
            body = new string(aescoder.decrypt(base64.getdecoder().decode(stringbuilder.tostring())));
        } catch (exception ex) {
  
            logger.error("报错请求接口的路径:{}",request.getrequesturi());
            logger.error("请求入参aes解密失败:{}",ex);
        } finally {
  
            if (bufferedreader != null) {
  
                try {
  
                    bufferedreader.close();
                } catch (ioexception ex) {
  
                    logger.error("请求入参base64转换失败:{}",ex);
                }
            }
        }
    }
    public string getbody() {
  
        return body;
    }
}

aescoder 加解密类

注意加密私钥密码必须为16位 字母和数字均可

import javax.crypto.cipher;
import javax.crypto.spec.secretkeyspec;
import java.nio.charset.standardcharsets;
import java.security.key;
import java.util.base64;
/**
 * aes加解密处理工具类
 * @author liang
 */
public class aescoder {
  
    /**
     * 加密方式 用aes
     */
    private static final string key_algorithm = "aes";
    /**
     * 默认的加密算法
     */
    private static final string default_cipher_algorithm = "aes/ecb/pkcs5padding";
    /**
     * 加密私钥密码
     */
    private static final string private_key = "wrqiorduis589756";
    static key key;
    static {
  
        key = tokey(initsecretkey());
    }
    /**
     * 私钥转换为byte数组
     * @return 私钥的byte数组
     */
    private static byte[] initsecretkey() {
  
        return private_key.getbytes(standardcharsets.utf_8);
    }
    private static key tokey(byte[] key){
  
        //生成密钥
        return new secretkeyspec(key, key_algorithm);
    }
    public static byte[] encrypt(byte[] data) throws exception{
  
        //实例化
        cipher cipher = cipher.getinstance(default_cipher_algorithm);
        //使用密钥初始化,设置为加密模式
        cipher.init(cipher.encrypt_mode, key);
        //执行操作
        return cipher.dofinal(data);
    }
    public static byte[] decrypt(byte[] data) throws exception{
  
        //实例化
        cipher cipher = cipher.getinstance(default_cipher_algorithm);
        //使用密钥初始化,设置为解密模式
        cipher.init(cipher.decrypt_mode, key);
        //执行操作
        return cipher.dofinal(data);
    }
    private static string  showbytearray(byte[] data){
  
        if(null == data){
  
            return null;
        }
        stringbuilder sb = new stringbuilder("{");
        for(byte b:data){
  
            sb.append(b).append(",");
        }
        sb.deletecharat(sb.length()-1);
        sb.append("}");
        return sb.tostring();
    }
    public static void main(string[] args) throws exception {
  
        string data ="aes数据阿斯器具";
        system.out.println("加密前数据: string:" data);
        system.out.println();
        byte[] encryptdata = encrypt(data.getbytes());
        string base64 = base64.getencoder().encodetostring(encryptdata);
        system.out.println("base64 string" base64);
        system.out.println();
        byte[] base64data = base64.getdecoder().decode(base64);
        byte[] decryptdata = decrypt(base64data);
        system.out.println("解密后数据: string:" new string(decryptdata, standardcharsets.utf_8));
    }
}

到此为止接口传参的数据解密就完成了 接下来就是返回数据的加密

dealwithresponsebody 全局数据加密类

注意这个注解 @controlleradvice 它的作用 传送门
这个responsebodyadvice接口的作用是重写beforebodywrite方法对返回体做一些自定义操作 此处我们可以对数据进行aes加密

import com.alibaba.fastjson.jsonobject;
import com.alibaba.fastjson.serializer.serializerfeature;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.core.methodparameter;
import org.springframework.http.mediatype;
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 java.util.base64;
/**
 * @author liang
 */
@controlleradvice
public class dealwithresponsebody implements responsebodyadvice {
  
    private static final logger logger = loggerfactory.getlogger(dealwithresponsebody.class);
    private static final string url = "/medmanage";
    @override
    public boolean supports(methodparameter returntype, class convertertype) {
  
        return true;
    }
    @override
    public object beforebodywrite(object body, methodparameter returntype, mediatype selectedcontenttype, class selectedconvertertype,
                                  serverhttprequest request, serverhttpresponse response) {
  
        string requestpath = request.geturi().getpath();
        //判断是否 需要处理的接口
        if(requestpath.startswith(url) && !requestpath.contains("upload")){
  
            try {
  
                //这里小心点别oom 谁在写循环引用 小心我给你寄刀片
                return base64.getencoder().encodetostring(aescoder.encrypt(jsonobject.tojsonstring(body, serializerfeature.writemapnullvalue,serializerfeature.disablecircularreferencedetect).getbytes()));
            } catch (exception e) {
  
                logger.error("返回结果的加密失败:{}",e);
            }
        }
        return body;
    }
}

最终总结

  • spring中的拦截器真的很好用,它体现出来的设计思想就是讲业务代码和逻辑处理分层开,设计模式真的能让人受用终生,它不仅仅体现在编程中,做事学习都有可以借鉴的地方
网站地图