1. 程式人生 > >SpringCloud Zuul修改請求引數資訊

SpringCloud Zuul修改請求引數資訊

Zuul作為閘道器服務,是其他各服務對外中轉站,通過Zuul進行請求轉發。這就涉及到部分資料是不能原封返回的,比如服務之間通訊的憑證,使用者的加密資訊等等。
對返回的資訊加密後,客戶端請求其他服務時,閘道器就需要將原來加密的資訊解密後轉發到對應的服務中。
解密的功能其實可以理解成是許可權認證的過程,將合法的請求轉發到對應的服務,將非法的請求直接攔截在閘道器層。這一部分其實也是可以使用許可權框架Shiro和Spring Security等。

本文中的程式碼已提交至: https://gitee.com/cmlbeliever/springcloud 歡迎Star
實現類在:api-getway工程下的com.cml.springcloud.api.filter.AccessTokenFilter

在上文(http://blog.csdn.net/cml_blog/article/details/78349703)中使用者登入返回的token在閘道器層加密後返回,那麼訪問使用者其他相關的介面時就需要將加密的token解密後傳入到使用者服務中。這裡使用到的是Zuul的過濾器。

如果請求是非法的,那麼就沒有必要繼續轉發了,直接跳過其他型別的過濾器,返回錯誤資訊。所以這裡使用的是pre型別的過濾器。程式碼如下:

public class AccessTokenFilter extends AbstractZuulFilter {

    private static final String PARAM_TOKEN = "token"
; @Value("${system.config.accessTokenFilter.ignore}") private String ignoreUrl; @Autowired private AuthApi authApi; private ResponseHandler responseHandler; @Override public Object run() { try { RequestContext context = getCurrentContext(); InputStream in = (InputStream) context.get("requestEntity"
); if (in == null) { in = context.getRequest().getInputStream(); } String token = context.getRequest().getParameter(PARAM_TOKEN); logger.info("accessToken:" + token); logger.info("params:" + context.getRequestQueryParams()); logger.info("contentLength:" + context.getRequest().getContentLength()); logger.info("contentType:" + context.getRequest().getContentType()); // 校驗token if (StringUtils.isNotBlank(token)) { AuthResult authResult = authApi.parseToken(token); // 校驗成功 if (authResult.isSuccess()) { String body = StreamUtils.copyToString(in, Charset.forName("UTF-8")); logger.info("body:" + body); body = StringUtils.replace(body, PARAM_TOKEN + "=" + token, PARAM_TOKEN + "=" + authResult.getToken()); logger.info("轉換後的body:" + body); // context.set("requestEntity", new // ByteArrayInputStream(body.getBytes("UTF-8"))); final byte[] reqBodyBytes = body.getBytes(); context.setRequest(new HttpServletRequestWrapper(getCurrentContext().getRequest()) { @Override public ServletInputStream getInputStream() throws IOException { return new ServletInputStreamWrapper(reqBodyBytes); } @Override public int getContentLength() { return reqBodyBytes.length; } @Override public long getContentLengthLong() { return reqBodyBytes.length; } }); return null; } } if (responseHandler != null) { context.getResponse().setCharacterEncoding("UTF-8"); context.setResponseStatusCode(responseHandler.getResponseCode()); context.setResponseBody(responseHandler.getResponseBody(null, null)); } context.setSendZuulResponse(false); } catch (IOException e) { rethrowRuntimeException(e); } return null; } @Override public boolean shouldFilter() { HttpServletRequest req = RequestContext.getCurrentContext().getRequest(); logger.info("" + ignoreUrl + "," + req.getRequestURI().toString()); return StringUtils.equalsIgnoreCase(req.getMethod(), "post") && !StringUtils.contains(ignoreUrl, req.getRequestURI().toString()); } @Override public int filterOrder() { return 0; } public ResponseHandler getResponseHandler() { return responseHandler; } public void setResponseHandler(ResponseHandler responseHandler) { this.responseHandler = responseHandler; } @Override public String filterType() { return "pre"; } }
@Component
    public class AccessTokenResponseHandler implements ResponseHandler {

        @Value("${system.config.error.invalidToken}")
        private String invalidTokenMessage;

        @Override
        public int getResponseCode() {
            return HttpServletResponse.SC_OK;
        }

        @Override
        public String getResponseBody(String originMessage, Throwable e) {
            Gson gson = new Gson();
            Map<String, Object> result = new HashMap<String, Object>();
            result.put("status", HttpServletResponse.SC_BAD_REQUEST);
            result.put("message", invalidTokenMessage);
            return gson.toJson(result);
        }
    }

以上程式碼是將請求引數中的token解析出來,解析成功後替換原引數,轉發到使用者服務。解析失敗或者token為空,直接返回錯誤資訊。

啟動工程中的全部服務後,首先先呼叫登入介面:
這裡寫圖片描述

輸入相同的使用者名稱和密碼就認為是合法使用者,返回加密後的token,
獲取到token後,呼叫獲取使用者資訊的介面:getUserInfoByToken
這裡寫圖片描述
可以看到使用者的資訊正常返回了,通過log也可以看出到達使用者服務的token是解密後的資訊。
輸入錯誤的token:
這裡寫圖片描述

返回錯誤資訊。
至此閘道器層修改請求引數功能實現ok。

總結:
Zuul 過濾器可以攔截使用者請求和返回資訊,自定義請求引數和自定義返回,返回的資訊有些是不能直接返回到客戶端的,就需要加密處理。但是訪問其他介面,設計到許可權處理,許可權相關的還是推薦使用專門的許可權處理框架Shiro或SpringSecurity。
這裡的修改請求引數只是作為學習的例子,演示Zuul過濾器的功能。在實際情況下不推薦在Zuul 過濾器中做許可權校驗。倒是可以在過濾器中打印出請求和返回的log作為開發除錯使用。

本文中的程式碼已提交至: https://gitee.com/cmlbeliever/springcloud 歡迎Star
實現類在:api-getway工程下的com.cml.springcloud.api.filter.AccessTokenFilter