zoukankan      html  css  js  c++  java
  • 通过zuul修改请求参数——对请求参数进行解密

    zuul是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用,Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架,Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。zuul的核心是一系列的filters, 其作用可以类比Servlet框架的Filter,或者AOP。

    在基于 springcloud 构建的微服务系统中,通常使用网关zuul来进行一些用户验证等过滤的操作,比如 用户在 header 或者 url 参数中存放了 token ,网关层需要 用该 token 查出用户 的 userId ,并存放于 request 中,以便后续微服务可以直接使用而避免再去用 token 查询。

    在这里,使用zuul的过滤器对请求参数验签(解密),然后发给后续的微服务。

    共三个服务:注册中心,zuul服务,通过zuul能访问到的服务。

    流程:zuul服务和另一个服务注册到注册中心上,带有加密过得参数的请求url经过zuul处理参数解密之后发给后续微服务。

    首先获取到request,但是在request中只有getParameter()而没有setParameter()方法,所以直接修改url参数不可行,另外在request中虽然可以setAttribute(),但是可能由于作用域(request)的不同,一台服务器才能getAttribute()出来,在这里设置的Attribute在后续的微服务中是获取不到的,因此必须考虑另外的方式:get方法和其他方法处理方式不同,post和put需重写HttpServletRequestWrapper,即获取请求的输入流,重写json参数,传入重写构造上下文中的request中。

    zuul中的filter代码

    import com.example.zuuldemo.util.AESUtil;
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.http.ServletInputStreamWrapper;
    import net.sf.json.JSONObject;
    import org.apache.commons.lang.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StreamUtils;
    
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.IOException;
    import java.io.InputStream;
    import java.nio.charset.Charset;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 处理请求参数filter
     *
     * @author :liuqi
     * @date :2018-08-29 14:11.
     */
    @Component
    public class SignFilter extends ZuulFilter {
    
        private static Logger log = LoggerFactory.getLogger(SignFilter.class);
    
        /**
         * pre:路由之前
         * routing:路由之时
         * post: 路由之后
         * error:发送错误调用
         *
         * @return
         */
        @Override
        public String filterType() {
            return "pre";
        }
    
        /**
         * filterOrder:过滤的顺序
         *
         * @return
         */
        @Override
        public int filterOrder() {
            return 0;
        }
    
        /**
         * shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤
         *
         * @return
         */
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        /**
         * run:过滤器的具体逻辑。
         * 要把请求参数进行验签(解密)之后传给后续的微服务,首先获取到request,但是在request中只有getParameter()而没有setParameter()方法
         * 所以直接修改url参数不可行,另外在reqeust中虽然可以使用setAttribute(),但是可能由于作用域(request)的不同,一台服务器中才能getAttribute
         * 在这里设置的attribute在后续的微服务中是获取不到的,因此必须考虑另外的方式:即获取请求的输入流,并重写,即重写json参数,
         * ctx.setRequest(new HttpServletRequestWrapper(request) {}),这种方式可重新构造上下文中的request
         *
         * @return
         */
        @Override
        public Object run() {
    
            // 获取到request
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            // 获取请求参数name
            String name = "";
            try {
    
                // 请求方法
                String method = request.getMethod();
                log.info(String.format("%s >>> %s", method, request.getRequestURL().toString()));
                // 获取请求的输入流
                InputStream in = request.getInputStream();
                String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
                // 如果body为空初始化为空json
                if (StringUtils.isBlank(body)) {
                    body = "{}";
                }
                log.info("body" + body);
                // 转化成json
                JSONObject json = JSONObject.fromObject(body);
    
                // get方法和post、put方法处理方式不同
                if ("GET".equals(method)) {
    
                    // 获取请求参数name
                    name = request.getParameter("name");
    
                    if (name != null) {
                        // 关键步骤,一定要get一下,下面才能取到值requestQueryParams
                        request.getParameterMap();
                        Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams();
                        if (requestQueryParams == null) {
                            requestQueryParams = new HashMap<>();
                        }
                        List<String> arrayList = new ArrayList<>();
                        String key = "key";
                        String aes_decodedStr = AESUtil.getInstance().decode(name, key);
                        arrayList.add(aes_decodedStr + "");
                        requestQueryParams.put("decodename", arrayList);
                        ctx.setRequestQueryParams(requestQueryParams);
                    }
                }// post和put需重写HttpServletRequestWrapper
                else if ("POST".equals(method) || "PUT".equals(method)) {
    
                    // 获取请求参数name
                    name = json.getString("name");
    
                    if (name != null) {
    
                        String key = "key";
    //                String aes_encodedStr = AESUtil.getInstance().encode(name, key);
    //                log.info("加密:" + aes_encodedStr);
    //                json.put("decodename", aes_decodedStr);
                        String aes_decodedStr = AESUtil.getInstance().decode(name, key);
                        log.info("解密:" + aes_decodedStr);
    
                        // 把解密之后的参数放到json里
                        json.put("decodename", aes_decodedStr);
                        String newBody = json.toString();
                        log.info("newBody" + newBody);
                        final byte[] reqBodyBytes = newBody.getBytes();
    
                        // 重写上下文的HttpServletRequestWrapper
                        ctx.setRequest(new HttpServletRequestWrapper(request) {
                            @Override
                            public ServletInputStream getInputStream() throws IOException {
                                return new ServletInputStreamWrapper(reqBodyBytes);
                            }
    
                            @Override
                            public int getContentLength() {
                                return reqBodyBytes.length;
                            }
    
                            @Override
                            public long getContentLengthLong() {
                                return reqBodyBytes.length;
                            }
                        });
                    }
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    后续服务获取到解密后的参数

    import net.sf.json.JSONObject;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.util.StreamUtils;
    import org.springframework.web.bind.annotation.*;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    import java.io.InputStream;
    import java.nio.charset.Charset;
    import java.util.Map;
    
    /**
     * 接收经过zuul处理(解密)的参数,并返回
     *
     * @author :liuqi
     * @date :2018-08-29 12:12.
     */
    @RestController
    public class HiController {
    
        /**
         * get方式
         * @RequestParam注解方式
         *
         * @param decodename
         * @return
         */
        @GetMapping("/hi")
        public String getName(@RequestParam("decodename") String decodename){
            return decodename;
        }
    
        /**
         * post方式
         * @RequestBody注解方式获取
         *
         * @param param
         * @return
         */
        @PostMapping("/hello")
        public String postName(@RequestBody Map<String,String> param){
            String name = param.get("decodename");
            return name;
        }
    
        /**
         * post方式
         * 获取请求的输入流,并转化成json
         *
         * @param request
         * @return
         */
        @PostMapping("/hello1")
        public String postName1(HttpServletRequest request){
            String name = "";
            try {
                InputStream in = request.getInputStream();
                String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
                if(StringUtils.isNotBlank(body)){
                    JSONObject jsonObject = JSONObject.fromObject(body);
                    name = (String)jsonObject.get("decodename");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return name;
        }
    
    
        /**
         * post方式
         * @RequestBody注解方式获取
         *
         * @param param
         * @return
         */
        @PutMapping("/howareyou")
        public String putName(@RequestBody Map<String,String> param){
            String name = param.get("decodename");
            return name;
        }

    get请求测试

    地址:http://localhost:1112/api-a/hi?name=5A0B6501B76A82FCAE5FC26DB2583B0D

    post请求测试

    http://localhost:1112/api-a/hello

    put请求测试

    地址:http://localhost:1112/api-a/howareyou

     代码地址:https://github.com/yuki9467/zuul-handlerequest-demo

  • 相关阅读:
    STL实现的底层数据结构简介
    C++ STL中Map的按Key排序和按Value排序
    algorithm库介绍之---- stable_sort()方法 与 sort()方法 .
    git取消更改 恢复版本命令
    unbuntu下清理磁盘空间
    x265编码命令
    SQLServer数据库获取重复记录中日期最新的记录
    牛逼哄哄的 Lambda 表达式,简洁优雅就是生产力!
    哎!又要过年了,程序员最怕问到什么?
    swagger 使用指南
  • 原文地址:https://www.cnblogs.com/yuki67/p/9561525.html
Copyright © 2011-2022 走看看