资讯专栏INFORMATION COLUMN

web开发安全之请求及返回流数据加解密实践

Taonce / 2780人阅读

摘要:以上为加解密在实际开发过程中代码,代码提交是对于和等符号进行过滤,防止注入,在开发过程中可以参考此代码进行适当修改进行使用

web开发过程中对post请求过来的整个请求流数据,怎样保证post在传输过程中被截取后无法获取到用户提交请求实际数据,保证请求安全,在实践过程中我们采用过滤器(Filter)来实现流截取完成这个代码post请求流数据及返回数据加解密

一、请求数据流解密

1 请求数据提交filter进行数据过滤,过滤器类主要用于创建HttpServletRequest,实现解密,将解密后的request返回到下层

    public class CharacterFilter implements Filter{
    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
                      FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest)req;
                 TsRequest wrapRequest= new TsRequest(request,request.getParameterMap());
                chain.doFilter(wrapRequest, res);
             }
   }

2、提交数据解密:前端或者app端对提交数据流数据解密,继承HttpServletRequestWrapper方法实现自己的HttpServletRequest,拷贝原始数据流,getParameterValues写入解密方法,Spring框架进行自动解包是会通过getParameterValues读取数据流,进行自动解包,将数据set到对象,完成数据解密过程(copy数据流主要解决流只能读取一次问题)

    public class TsRequest extends HttpServletRequestWrapper{
    private Map params;
    public static final int BUFFER_SIZE = 4096;
    private byte[] requestBody = null;

    public TsRequest(HttpServletRequest request, Map newParams) {
        super(request);
        this.params = newParams;
        // 缓存请求body 读取Post数据
        String method = request.getMethod();
        if (("POST").equals(request.getMethod())) {
            try {
                ByteArrayOutputStream out = copy(request.getInputStream());
                requestBody = out.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static ByteArrayOutputStream copy(InputStream in) throws IOException {
        String data = convertStreamToString(in);
        if (data != null && data.indexOf("time") == -1) {
            try {
                data = AESDataUtils.decrypt(data);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        byte bytes[] = data.getBytes("UTF-8");
        ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
        out.write(bytes, 0, bytes.length);
        out.flush();
        return out;
    }

    public static String convertStreamToString(InputStream is)
            throws UnsupportedEncodingException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is,
                "UTF-8"));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return sb.toString();
    }

    /**
     * 重写 getInputStream()
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (requestBody == null) {
            requestBody = new byte[0];
        }
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

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

    public Map getParameterMap() {
        return params;
    }

    public Enumeration getParameterNames() {
        Vector l = new Vector(params.keySet());
        return l.elements();
    }

    public String[] getParameterValues(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String[]) {
            String[] value = (String[]) v;
            for (int i = 0; i < value.length; i++) {
                // AES解密数据
            value[i]=AESDataUtils.decrypt(value[i]);
                if (StringUtils.isNoneEmpty(value[i])) {
                    value[i] = value[i].replaceAll("<", "<");
                    value[i] = value[i].replaceAll(">", ">");
                }
            }
            return (String[]) value;
        } else if (v instanceof String) {
            String value = (String) v;
         value=AESDataUtils.decrypt(value);
            // AES解密数据
            if (StringUtils.isNoneBlank(value)) {
                value = value.replaceAll("<", "<");
                value = value.replaceAll(">", ">");
            }
            return new String[] { (String) value };
        } else {
            return new String[] { v.toString() };
        }
    }

    public String getParameter(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String) {
            String value = (String) v;
            value = AESDataUtils.decrypt(value);
            // AES解密数据
            if (StringUtils.isNoneBlank(value)) {
                value = value.replaceAll("<", "<");
                value = value.replaceAll(">", ">");
            }
            return (String) value;
        } else if (v instanceof String[]) {
            String[] strArr = (String[]) v;
            if (strArr.length > 0) {
                String value = strArr[0];
                String aesdata=AESDataUtils.decrypt(value);
                if(StringUtils.isNoneEmpty(aesdata)){
                    value =aesdata ;
                }
                // AES解密数据
                if (StringUtils.isNoneBlank(value)) {
                    value = value.replaceAll("<", "<");
                    value = value.replaceAll(">", ">");
                }
                return value;
            } else {
                return null;
            }
        } else {
            return v.toString();
        }
    }

}
二、返回数据流加密

1、继承过滤器接口实现返回数据过滤拦截,将返回数据转换为代理类,通过代理类改变处理返回数据流进行加密并将加密后数据重新写入到流中返回(使用代理类主要是为了解决流只能读取一次问题)

    public class ResponseFilter implements Filter{
     @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain filterChain) throws IOException, ServletException {
            filterChain.doFilter(request, response);
            ResponseWrapper wrapperResponse = new ResponseWrapper(
                    (HttpServletResponse) response);// 转换成代理类
            // 这里只拦截返回,直接让请求过去,如果在请求前有处理,可以在这里处理
            filterChain.doFilter(request, wrapperResponse);
            byte[] content = wrapperResponse.getContent();// 获取返回值
            if (content.length > 0) {

                String str = new String(content, "UTF-8");
                String ciphertext = null;

                try {
                    ciphertext = AESDataUtils.encrypt(str);
                    // ......根据需要处理返回值
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //将加密后数据重新写如后刷新数据流
                ServletOutputStream out = response.getOutputStream();
                out.write(ciphertext.getBytes());
                out.flush();
            }
    }
}

2.返回值输出代理,通过代理获取返回值信息内容

public class ResponseWrapper extends HttpServletResponseWrapper  
{  
  
    private ByteArrayOutputStream buffer;  
  
    private ServletOutputStream out;  
  
    public ResponseWrapper(HttpServletResponse httpServletResponse)  
    {  
        super(httpServletResponse);  
        buffer = new ByteArrayOutputStream();  
        out = new WrapperOutputStream(buffer);  
    }  
  
    @Override  
    public ServletOutputStream getOutputStream()  
        throws IOException  
    {  
        return out;  
    }  
  
    @Override  
    public void flushBuffer()  
        throws IOException  
    {  
        if (out != null)  
        {  
            out.flush();  
        }  
    }  
  
    public byte[] getContent()  
        throws IOException  
    {  
        flushBuffer();  
        return buffer.toByteArray();  
    }  
  
    class WrapperOutputStream extends ServletOutputStream  
    {  
        private ByteArrayOutputStream bos;  
  
        public WrapperOutputStream(ByteArrayOutputStream bos)  
        {  
            this.bos = bos;  
        }  
  
        @Override  
        public void write(int b)  
            throws IOException  
        {  
            bos.write(b);  
        }  
    }  
  
}  
三、使用工具类代码

AES加密工具类加解密代码模块,主要http请求加密过程会进行URL编码传入,需要进行URLDecoder.decode进行解码后才能进行解密操作

     public class AESDataUtils {
     /**
     * 解密
     * @param value
     * @return
     */
            public static String decrypt(String value){
                if(StringUtils.isNoneEmpty(value)){
                    if(value.indexOf("%")>-1){
                        value=URLDecoder.decode(value);
                    }
                }
                try { 
                    return AESUtils.decrypt(value);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return null;
            }
            /**
             * 加密
             * @param value
             * @return
             */
            public static String encrypt(String value){
                value=AESUtils.encrypt(value);
                value=URLEncoder.encode(value);
                return value;
            }
}

实际AES加解密代码块,本文档采用aes进行加密,也可以自行可以换其它加密方式进行加密

    public class AESUtils {
    private static String secretKey = "秘钥";
    private static String ivParameter = "";
    
    /**
     * aes 解密
     * 
     * @param sSrc
     * @return
     * @throws Exception
     */
    public static String decrypt(String sSrc) throws Exception {
        try {
            byte[] raw = secretKey.getBytes("ASCII");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES / CBC / PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original, "UTF-8");
            return originalString;
        } catch (Exception ex) {
            return null;
        }
    }
    
     //加密
    public static String encrypt(String sSrc) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] raw = secretKey.getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            //使用CBC模式,需要一个向量iv,可增加加密算法的强度
            IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
            //此处使用BASE64做转码。
            return new BASE64Encoder().encode(encrypted);
        } catch (Exception ex) {
            return null;
        }
    }

}

以上为加解密在实际开发过程中代码,代码提交是对于>和<等符号进行过滤,防止sql注入,在开发过程中可以参考此代码进行适当修改进行使用

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71066.html

相关文章

  • web开发安全请求返回数据解密实践

    摘要:以上为加解密在实际开发过程中代码,代码提交是对于和等符号进行过滤,防止注入,在开发过程中可以参考此代码进行适当修改进行使用 web开发过程中对post请求过来的整个请求流数据,怎样保证post在传输过程中被截取后无法获取到用户提交请求实际数据,保证请求安全,在实践过程中我们采用过滤器(Filter)来实现流截取完成这个代码post请求流数据及返回数据加解密 一、请求数据流解密 1 请求...

    Shonim 评论0 收藏0
  • 墙裂推荐:搜云库技术团队,面试必备的技术干货

    摘要:今天整理了一下近大半年以来的一些文章,和我的预期一样,很多文章我都忘记自己曾经写过了,这个记录的过程让我也有了新的理解。希望大家,收藏,点赞,加转发。 今天整理了一下近大半年以来的一些文章,和我的预期一样,很多文章我都忘记自己曾经写过了,这个记录的过程让我也有了新的理解。希望大家,收藏,点赞,加转发。 面试必备 面试必备:深入Spring MVC DispatchServlet 源码...

    SegmentFault 评论0 收藏0
  • 墙裂推荐:搜云库技术团队,面试必备的技术干货

    摘要:今天整理了一下近大半年以来的一些文章,和我的预期一样,很多文章我都忘记自己曾经写过了,这个记录的过程让我也有了新的理解。希望大家,收藏,点赞,加转发。 今天整理了一下近大半年以来的一些文章,和我的预期一样,很多文章我都忘记自己曾经写过了,这个记录的过程让我也有了新的理解。希望大家,收藏,点赞,加转发。 面试必备 面试必备:深入Spring MVC DispatchServlet 源码...

    Neilyo 评论0 收藏0
  • 微服务分布式文件系统

    摘要:于是便诞生了随行付分布式文件系统简称,提供的海量安全低成本高可靠的云存储服务。子系统相关流程图如下核心实现主要为随行付各个业务系统提供文件共享和访问服务,并且可以按应用统计流量命中率空间等指标。 背景 传统Web应用中所有的功能部署在一起,图片、文件也在一台服务器;应用微服务架构后,服务之间的图片共享通过FTP+Nginx静态资源的方式进行访问,文件共享通过nfs磁盘挂载的方式进行访问...

    stormjun 评论0 收藏0
  • 鹅厂干货 | 腾讯游戏APP协议迭代的那些事

    摘要:本文则主要总结了心悦俱乐部的接入层从文本协议到二进制协议迭代过程中的技术方案,包括协议规范安全性等方面的内容。在心悦的文本协议方案中,采用的是对请求数据进行模式的加密。包括明文的协议包头和密文的二进制流。 欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~。 作者:罗广镇 | 腾讯移动开发工程师 App与后台通信通常有采用json等文本协议或者采用二进制协议,本文则主要总结了心...

    luck 评论0 收藏0

发表评论

0条评论

Taonce

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<