1、简介
Zuul是Netflix开源的微服务网关,可以和Eureka、Feign、Hystrix等组件配合使用。Zuul核心是一系列的过滤器,可以完成很多的功能。
- 身份认证与安全:识别每个资源的验证要求,并拒绝与要求不符的请求
- 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图。
- 动态路由:动态地将请求路由到不同不同的后端集群
- 压力测试:逐渐增加指向集群的流量,以了解性能
- 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
- 静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到内部集群
- 多区域弹性:跨越AWS Region进行请求路由,旨在实现ELB使用的多样化,以及让系统的边缘更贴近系统的使用者
SpringCloud对Zuul进行了整合与增强, 不管是来自客户端的请求还是服务端内部自己调用,一切对服务的请求都会经过Zuul网关,然后由网关来实现鉴权、动态路由等等操作。Zuul就是服务的统一入口。
2、过滤器
Zuul作为网关中的一个重要功能,目的就是要实现请求的鉴权,这往往是通过Zuul提供的过滤器来实现的。
2.1、ZuulFilter
ZuulFilter是过滤器的最高级父类,其中有四个重要的方法。
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
abstract public String filterType();//过滤器类型
abstract public int filterOrder();//过滤器顺序
boolean shouldFilter();// 来自IZuulFilter,要不要过滤
Object run() throws ZuulException;// IZuulFilter,过滤逻辑
}
- shouldFilter:返回的是一个boolean值,判断该过滤器是否执行,返回true表示执行
- run:过滤器的具体业务逻辑
- filterType:返回的是字符串,代表是过滤器的类型,其中包括四种类型:
pre:请求在路由前执行
route:路由请求时调用
post:在routing和error过滤器之后调用
error:处理请求时发生错误时调用 - filterOrder:通过返回的int值来决定过滤器的执行顺序,数字越小优先级越高
2.2、自定义过滤器
首先导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
在yml文件中进行配置
server:
port: 10011
spring:
application:
name: tensquare-manager
eureka:
client:
service-url:
defaultZone: http://localhost:8888/eureka/
instance:
prefer-ip-address: true
zuul:
routes:
tensquare-base:
path: /base/**
serviceId: tensquare-base
tensquare-article:
path: /article/**
serviceId: tensquare-article
tensquare-user:
path: /user/**
serviceId: tensquare-user
如果我们需要转发Authorization给该有的模块,做权限验证的话,就需要获得到Http请求的上下文,然后再传递给转发的模块。于是我们可以写一个在前台专门来获取request上下文并且转发的WebFilter。
@Component
public class WebFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
//得到 request上下文
RequestContext currentContext = RequestContext.getCurrentContext();
//得到request域
HttpServletRequest request = currentContext.getRequest();
//得到头信息
String header = request.getHeader("Authorization");
//判断是否真的有头信息
System.out.println(header);
if (header != null && !"".equals(header)) {
//把头信息继续向下传
currentContext.addZuulRequestHeader("Authorization", header);
}
return null;
}
}
获得了头信息间接的获取到了token给后端,现在要去把token解析出来,获取到用户的角色信心、权限信息等等。
使用无状态的jwt整合SpringSecurity去进行加密。
JWTUtil的工具类,yml配置等等就不说了,SpringSecurity整合jwt的无状态认证授权可以参考这篇文章。整合SpringSecurity随机加密的无状态认证授权
将Authorization的Header头信息传递下去,接下来需要对token进行解析,然后进行统一配置。通过getRole来获取到角色,然后进行统一的拦截就是相当于一个过滤器,因此将刚才的代码写到过滤器里面,写经由WebFilter传递token的后台过滤器ManagerFilter。
@Component
public class ManagerFilter extends ZuulFilter {
@Autowired
private JwtUtil jwtUtil;
/**
* 过滤是在请求前pre还是请求后post执行
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器执行的顺序,数字越小越先执行
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 当前过滤器是否开启
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器内执行的逻辑,return任何object值都表示继续执行
* setsendzullResponse(false)表示不继续执行
*/
@Override
public Object run() throws ZuulException {
System.out.println("经过了过滤器");
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
if (request.getMethod().equals("OPTIONS")) {
return null;
}
if (request.getRequestURL().indexOf("login") > 0) {
return null;
}
String header = request.getHeader("Authorization");
if (header != null && !"".equals(header)) {
if (header.startsWith("Broker ")) {
String token = header.substring(7);
try {
Claims claims = jwtUtil.parseJWT(token);
String roles = (String) claims.get("roles");
if (roles.equals("admin")) {
currentContext.addZuulRequestHeader("Authorization", header);
return null;
}
}catch (Exception e){
e.printStackTrace();
currentContext.setSendZuulResponse(false);
}
}
}
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(403);
currentContext.setResponseBody("权限不足");
currentContext.getResponse().setContentType("text/html; charset=utf-8");
return null;
}
}
启动类:
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ManagerApplication {
public static void main(String[] args) {
SpringApplication.run(ManagerApplication.class, args);
}
@Bean
public JwtUtil jwtUtil(){
return new JwtUtil();
}
}
测试成功: