addZuulRequestHeader 中文乱码

通过 RequestContext.addZuulRequestHeader 将用户信息的 JSON 格式数据传递给后端服务时中文出现了乱码。尝试设置了几个 Zuul 的配置项,没起作用,于是准备将数据改为使用 Base64 格式传递来避免乱码问题。

1. 将 JSON 字符串转为 Base64 格式的字符串:

import org.springframework.util.Base64Utils;

ctx.addZuulRequestHeader(RequestHeader.SSO_TOKEN, 
    Base64Utils.encodeToString(
        objectMapper.toJSONString(ssoToken).getBytes(StandardCharsets.UTF_8)
    )
);
关于 Zuul + Nacos 自动注册路由

有个项目中使用了 Zuul + Nacos 实现的网关,服务启用了 discovery client( @EnableDiscoveryClient ),默认情况下会动态的为注册到 Nacos 的服务配置一个同名的路由。

自动注册路由的具体处理可以参考 DiscoveryClientRouteLocatorCompositeDiscoveryClientNacosDiscoveryClient 这几个类。

Zuul 网关登录验证

使用过滤器(ZuulFilter)实现登录验证。

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class AuthFilter extends ZuulFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger((AuthFilter.class));

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();

        // 返回 false 则不会执行 run 方法
        // 可以根据 request 信息判断是否需要验证
        // 比如,过滤请求地址结尾为 .json 的请求
        String url = request.getRequestURI();
        if (url.endsWith(".json")) {
            return false;
        }

        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();

        LOGGER.info(String.format("send %s request to %s", request.getMethod(), request.getRequestURL().toString()));

        // 可以从 Header 中获取令牌,执行验证处理
        // 也可以获取 request 中的请求参数来验证
        // 这里只是一个简单的非空验证,正常的是执行一些解密和令牌时间是否过期之类的验证
        if (StringUtils.isBlank(request.getHeader("token"))) {
            //设置为 false 则不往下走 (不调用 api 接口)
            ctx.setSendZuulResponse(false);
            //响应一个状态码:401
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            // response.setStatus(HttpStatus.UNAUTHORIZED.value());

            // 也可以返回正常结束的请求,然后执行业务处理
            // response.setStatus(HttpStatus.OK.value());
            // response.setCharacterEncoding("utf-8");
            // ctx.setResponseBody("{\"isSuccess\":false,\"msg\":\"请先登录。\",\"ErrorCode\":401}");
        }

        return null;
    }
}