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修改请求参数——对请求参数进行解密的更多相关文章
-
java 修改HttpServletRequest的参数或请求头
场景:过滤器中获取参数Token并添加到请求头(用户认证兼容老系统) 请求头和请求参数是不能直接修改,也没有提供修改的方法,但是可以在过滤器和拦截器中使用HttpServletRequestWrapp ...
-
Zuul 修改 请求头、响应头 (死磕)
疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...
-
SpringMVC(二):RequestMapping修饰类、指定请求方式、请求参数或请求头、支持Ant路径
@RequestMapping用来映射请求:RequestMapping可以修饰方法外,还可以修饰类 1)SpringMVC使用@RequestMapping注解为控制指定可以处理哪些URL请求: 2 ...
-
【nginx笔记】系统参数设置-使Nginx支持更多并发请求的TCP网络参数
首先,需要修改/etc/sysctl.conf来更改内核参数.例如,最常用的配置: fs.file-max = 999999 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tc ...
-
spring基础---->;请求与响应的参数(一)
这里面我们主要介绍一下spring中关于请求和响应参数数据的问题.爱,从来就是一件千回百转的事.不曾被离弃,不曾受伤害,怎懂得爱人?爱,原来是一种经历. spring中的请求与响应 一.spring中 ...
-
rest-assured之如何指定请求数据(Specifying Request Data 包括请求参数、请求头、cookie等)
我们除了可以为一个请求指定请求参数之外,还可以指定请求头(header).cookies.请求体(body)以及请求内容类型(content-type)等,下面我们就来一一介绍一下: 一.请求HTTP ...
-
Django中获取参数(路径,查询,请求头,请求体)
一.通常HTTP协议向服务器传参有几种途径 : 提取URL的特定部分,如/weather/shanghai/2018,可以在服务器端的路由中用正则表达式截取: 查询字符串(query string), ...
-
postman(十二):发送携带md5签名、随机数等参数的请求
想起来之前在借助百度翻译接口做翻译小工具的时候,需要把参数进行md5加密后再传输. 而在平时的接口测试工作中难免会遇到类似这种请求参数,比如md5加密.时间戳.随机数等等.固然可以先计算出准确的参数, ...
-
postman+xmysql实现postman与数据库的交互,获取数据库的值来作为参数进行请求
安装nodejs和npm详细步骤:https://www.runoob.com/nodejs/nodejs-install-setup.html 安装xmysql 执行命令: npm install ...
随机推荐
-
TIOBE Index for January 2016(转载)
Java has won the TIOBE Index programming language award of the year. This is because Java has the la ...
-
Asp.net 高性能 Sqlite ORM 框架之 sqliteSugar
一.介简 easyliter框架的升级版本,并且正式命名为SqliteSugar框架,另外Sugar系列还有 MySql和MsSql版本,Oracle版本待开发中(因为客户端太大一直在忧郁当中) 用S ...
-
IOS 取消表格单元格 TableViewCell 去掉高亮状态 点击Cell取消选择状态
以下是两种实现效果 1. 自定义cell 继承UITableViewCell 重写 -(void)setSelected:(BOOL)selected animated:(BOOL)animated ...
-
js基础之BOM
一.window.open 栗子:阿里西西运行代码功能 var oBtn = document.getElementById('btn1'); var oTxt = document.getEleme ...
-
Java学习笔记之:Java 流
一.介绍 Java.io包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io包中的流支持很多种格式,比如:基本类型.对象.本地化字符集等等. 一个流可以理解为一 ...
-
js学习笔记第二篇
Js笔记整理 1.StringAPI a) 大小写转换:str.toUpperCase();str.toLowerCase(); b) 获取指定位置字符: Str[i]-- ...
-
【转】linux设备驱动之MMC SD卡——核心层简单分析
原文网址:http://blog.chinaunix.net/uid-28685940-id-3889878.html /*************************************** ...
-
sublime text下载和汉化
好处就不说了,能认识到这款编辑器,基本上对它有一定的了解了. Sublime Text2是一款开源的软件,不需要注册即可使用(虽然没有注册会有弹窗,但是基本不影响使用). 官方网站:http://ww ...
-
Android项目导入时,出现的Could not write file 。。。。。。.classpath错误解决办法
导入到Eclipse中后选择了相应的API后,红叉的项目错误没有了. 工程列表也无任何错误了.但出现了这样的提示框错误 说明的是.classpath这个环境文件不能写.随后,查看工程文件主目录下的.c ...
-
Python基础之模块、数据类型及数据类型转换
一.模块 1.标准库 不需要安装,直接调入使用的模块. import sys模块: import sys print(sys.path) #打印环境变量绝对路径 print(sys.argv) #打印 ...