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

通过zuul修改请求参数——对请求参数进行解密的更多相关文章

  1. java 修改HttpServletRequest的参数或请求头

    场景:过滤器中获取参数Token并添加到请求头(用户认证兼容老系统) 请求头和请求参数是不能直接修改,也没有提供修改的方法,但是可以在过滤器和拦截器中使用HttpServletRequestWrapp ...

  2. Zuul 修改 请求头、响应头 (死磕)

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...

  3. SpringMVC(二):RequestMapping修饰类、指定请求方式、请求参数或请求头、支持Ant路径

    @RequestMapping用来映射请求:RequestMapping可以修饰方法外,还可以修饰类 1)SpringMVC使用@RequestMapping注解为控制指定可以处理哪些URL请求: 2 ...

  4. 【nginx笔记】系统参数设置-使Nginx支持更多并发请求的TCP网络参数

    首先,需要修改/etc/sysctl.conf来更改内核参数.例如,最常用的配置: fs.file-max = 999999 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tc ...

  5. spring基础---->请求与响应的参数(一)

    这里面我们主要介绍一下spring中关于请求和响应参数数据的问题.爱,从来就是一件千回百转的事.不曾被离弃,不曾受伤害,怎懂得爱人?爱,原来是一种经历. spring中的请求与响应 一.spring中 ...

  6. rest-assured之如何指定请求数据(Specifying Request Data 包括请求参数、请求头、cookie等)

    我们除了可以为一个请求指定请求参数之外,还可以指定请求头(header).cookies.请求体(body)以及请求内容类型(content-type)等,下面我们就来一一介绍一下: 一.请求HTTP ...

  7. Django中获取参数(路径,查询,请求头,请求体)

    一.通常HTTP协议向服务器传参有几种途径 : 提取URL的特定部分,如/weather/shanghai/2018,可以在服务器端的路由中用正则表达式截取: 查询字符串(query string), ...

  8. postman(十二):发送携带md5签名、随机数等参数的请求

    想起来之前在借助百度翻译接口做翻译小工具的时候,需要把参数进行md5加密后再传输. 而在平时的接口测试工作中难免会遇到类似这种请求参数,比如md5加密.时间戳.随机数等等.固然可以先计算出准确的参数, ...

  9. postman+xmysql实现postman与数据库的交互,获取数据库的值来作为参数进行请求

    安装nodejs和npm详细步骤:https://www.runoob.com/nodejs/nodejs-install-setup.html 安装xmysql 执行命令: npm install ...

随机推荐

  1. 一秒钟带你走进P图世界-----(python)PIL库的使用

    python-----PIL库的使用 一.什么是PIL库 1.PIL(Python Image Library)库是python语言的第三方库,具有强大的图像处理能力,不仅包含了丰富的像素.色彩操作功 ...

  2. JAVA集合1--总体框架

    JAVA集合是JAVA提供的工具包,包含了常用的数据结构:集合.链表.栈.队列.数组.映射等.JAVA集合工具包的位置是java.util.* JAVA集合主要可以分为4个部分:List.Set.Ma ...

  3. SimpleDateFormat 线程不安全及解决方案

    SimpleDateFormat定义 SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类.它允许进行格式化(日期 -> 文本).解析(文本 -> 日期 ...

  4. Mathematica 2

    如今的数值分析,如果没有高等代数的基础,都不好意思打招呼说自己是 "有数学基础". 高等代数,解决问题的一大神器. 1,初等变换 2,特征值 | A-λE | = | λE - A ...

  5. python学习第32天

    # 使用模块操作进程# multiprocessing 一个多元化的进程模块# multiple 多元化 processing 进程 # 涵盖了和进程相关的几乎所有的内容# Process类 帮助你启 ...

  6. GIt -- fatal: refusing to merge unrelated histories 问题处理

    今晚碰到这个问题-- fatal: refusing to merge unrelated histories 想了一下,为什么就这样了? 因为我是先本地创建了仓库,并添加了文件,然后再到github ...

  7. WPF 10天修炼 第三天- Application全局应用程序类

    Application对象 当一个WPF应用程序启动时,首先会实例化一个全局唯一的Application对象,类似于WinForm下的Application类,用于控制整个应用程序,该类将用于追踪应用 ...

  8. es6 总结知识点

    1. let  和 const 只在代码块中有效  {}   js块级作用域. const 定义的对象是可以改变其属性的 const a =[], b={} ; a.push(1); b.a=1; / ...

  9. 8266编译错误 xtensa-lx106-elf/bin/ld: segmentled section `.text' will not fit in region `iram1_0_seg'

    一种简单的解决办法 Okay, the solution was to copy the libgcc.a file from: esp-open-sdk/ESP8266_NONOS/lib/ to ...

  10. vs2017打包安卓项目简述

    1.使用vs2017创建安卓项目 2.选择Release模式编译项目 3.项目存档 4.签名分发 存档完成后,点击分发按钮进入如下界面 按要求输入签名信息 选择apk保存位置 5.分享apk文件 参考 ...