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 result = new HashMap();
            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


你可能感兴趣的:(微服务)