zuul是netflix開源的一個API Gateway 服務器, 本質上是一個web servlet應用,Zuul 在雲平臺上提供動態路由,監控,彈性,安全等邊緣服務的框架,Zuul 至關因而設備和 Netflix 流應用的 Web 網站後端全部請求的前門。zuul的核心是一系列的filters, 其做用能夠類比Servlet框架的Filter,或者AOP。java
在基於 springcloud 構建的微服務系統中,一般使用網關zuul來進行一些用戶驗證等過濾的操做,好比 用戶在 header 或者 url 參數中存放了 token ,網關層須要 用該 token 查出用戶 的 userId ,並存放於 request 中,以便後續微服務能夠直接使用而避免再去用 token 查詢。git
在這裏,使用zuul的過濾器對請求參數驗籤(解密),而後發給後續的微服務。github
共三個服務:註冊中心,zuul服務,經過zuul能訪問到的服務。web
流程:zuul服務和另外一個服務註冊到註冊中心上,帶有加密過得參數的請求url通過zuul處理參數解密以後發給後續微服務。spring
首先獲取到request,可是在request中只有getParameter()而沒有setParameter()方法,因此直接修改url參數不可行,另外在request中雖然能夠setAttribute(),可是可能因爲做用域(request)的不一樣,一臺服務器才能getAttribute()出來,在這裏設置的Attribute在後續的微服務中是獲取不到的,所以必須考慮另外的方式:get方法和其餘方法處理方式不一樣,post和put需重寫HttpServletRequestWrapper,即獲取請求的輸入流,重寫json參數,傳入重寫構造上下文中的request中。apache
zuul中的filter代碼json
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請求測試api
地址: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