zuul一般有两大作用,1是类似于Nginx的网址重定向,但zuul的重定向的一般是整个spring cloud里在Eureka注册中心的模块.
zuul: ignored-services: '*' sensitiveHeaders: routes: oauth: path: /api-o/** serviceId: oauth-center api-u: path: /api-u/** serviceId: user-center backend: path: /api-b/** serviceId: manage-backend log: path: /api-l/** serviceId: log-center file: path: /api-f/** serviceId: file-center sms: path: /api-n/** serviceId: notification-center
**的意思是可以匹配任何多级目录的意思.
*为单级目录
sensitiveHeaders过滤客户端附带的headers,如:
sensitiveHeaders: X-ABC
如果在发请求时带了X-ABC,那么X-ABC不会往下游服务传递。
2、zuul更重要的功能为过滤请求.
public class AccessFilter extends ZuulFilter { @Override public String filterType() { return null; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return false; } @Override public Object run() throws ZuulException { return null; } }
我们自定义一个过滤类,继承于ZuulFilter,一般要实现上面四个方法.
filterType:过滤器的类型.
pre
:可以在请求被路由之前调用route
:在路由请求时候被调用post
:在route和error过滤器之后被调用error
:处理请求时发生错误时被调用
在
org.springframework.cloud.netflix.zuul.filters.support.FilterConstants
中有这四种对应
public static final String ERROR_TYPE = "error"; public static final String POST_TYPE = "post"; public static final String PRE_TYPE = "pre"; public static final String ROUTE_TYPE = "route";
filterOrder:过滤器的执行顺序.当请求在一个阶段存在多个过滤器时,需要根据该方法返回的值来依次执行.
shouldFilter:判断该过滤器是否需要执行.
比如我们需要一个过滤条件,当包含"*-anon/internal*"的uri不允许外网通过网关调用,只允许微服务间在内网调用.我们可以这么写.
@Override public boolean shouldFilter() { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); return PatternMatchUtils.simpleMatch("*-anon/internal*", request.getRequestURI()); }
最后就是run:过滤器的具体逻辑.
@Override public Object run() { RequestContext requestContext = RequestContext.getCurrentContext(); requestContext.setResponseStatusCode(HttpStatus.FORBIDDEN.value()); requestContext.setResponseBody(HttpStatus.FORBIDDEN.getReasonPhrase()); requestContext.setSendZuulResponse(false); return null; }
返回403 Forbidden错误,通过requestContext.setSendZuulResponse(false)不进行路由.
请注意以上是只防外网的,内网的调用可以使用feign.比如说
@FeignClient("manage-backend") public interface BackendClient { @GetMapping("/backend-anon/internal/blackIPs") Set<String> findAllBlackIPs(@RequestParam("params") Map<String, Object> params); }
它是指向manage-backend模块的,而且@GetMapping("/backend-anon/internal/blackIPs")包含了"*-anon/internal*",即外网无法访问这个接口.具体实现为
@RestController public class BlackIPController { @Autowired private BlackIPService blackIPService; /** * 添加黑名单ip * * @param ip */ @LogAnnotation(module = LogModule.ADD_BLACK_IP) @PreAuthorize("hasAuthority('ip:black:save')") @PostMapping("/blackIPs") public void save(@RequestBody BlackIP blackIP) { blackIP.setCreateTime(new Date()); blackIPService.save(blackIP); } /** * 删除黑名单ip * * @param ip */ @LogAnnotation(module = LogModule.DELETE_BLACK_IP) @PreAuthorize("hasAuthority('ip:black:delete')") @DeleteMapping("/blackIPs/{ip}") public void delete(@PathVariable String ip) { blackIPService.delete(ip); } /** * 查询黑名单 * * @param params * @return */ @PreAuthorize("hasAuthority('ip:black:query')") @GetMapping("/blackIPs") public Page<BlackIP> findBlackIPs(@RequestParam Map<String, Object> params) { return blackIPService.findBlackIPs(params); } /** * 查询黑名单<br> * 可内网匿名访问 * * @param params * @return */ @GetMapping("/backend-anon/internal/blackIPs") public Set<String> findAllBlackIPs(@RequestParam Map<String, Object> params) { Page<BlackIP> page = blackIPService.findBlackIPs(params); if (page.getTotal() > 0) { return page.getData().stream().map(BlackIP::getIp).collect(Collectors.toSet()); } return Collections.emptySet(); } }
中的
@GetMapping("/backend-anon/internal/blackIPs") public Set<String> findAllBlackIPs(@RequestParam Map<String, Object> params) { Page<BlackIP> page = blackIPService.findBlackIPs(params); if (page.getTotal() > 0) { return page.getData().stream().map(BlackIP::getIp).collect(Collectors.toSet()); } return Collections.emptySet(); }
当然它是属于manage-backend模块.