目录
- 简介
- 加入zuul后的架构
- 快速入门
- 添加Zuul依赖
- 编写zuul启动类
- 编写zuul配置文件
- 编写路由规则
- 面向服务的路由
- 添加Eureka客户端依赖
- 开启Eureka客户端发现功能
- 添加Eureka配置,获取服务信息
- 修改映射配置,通过服务名称获取
- 简化的路由配置
- 过滤器
- 使用场景
- 自定义过滤器
简介
加入zuul后的架构
不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。
快速入门
创建zuul服务
添加Zuul依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
编写zuul启动类
通过 @EnableZuulProxy 注解开启Zuul的功能:
@SpringBootApplication
@EnableZuulProxy //开启zuul
public class ZuulServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServiceApplication.class);
}
}
编写zuul配置文件
server:
port: 10000
spring:
application:
name: zuul-service
编写路由规则
zuul:
routes:
user-service: #没用
path: /user-service/** # 服务实例ID
url: http://127.0.0.1:8080 # 服务代理的具体地址
order-service: #没用
path: /order-service/** # 服务实例ID
url: http://127.0.0.1:9090 # 服务代理的具体地址
面向服务的路由
在刚才的路由规则中,我们把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然就不合理了。
我们应该根据服务的名称,去Eureka注册中心查找 服务对应的所有实例列表,然后进行动态路由才对!
添加Eureka客户端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
开启Eureka客户端发现功能
在zuul服务启动类加注解@EnableDiscoveryClient //开启euerka客户端发现功能
@SpringBootApplication
@EnableDiscoveryClient //开启euerka客户端发现功能
@EnableZuulProxy //开启zuul
public class ZuulServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServiceApplication.class);
}
}
添加Eureka配置,获取服务信息
# Eureka配置-- 目前先注册自己保证不报错
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka
修改映射配置,通过服务名称获取
因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。
zuul:
routes:
order-service:
path: /1/**
#url: order-service #无负载
serviceId: order-service #有负载
简化的路由配置
我们关于user-service的配置可以简化为一条:
zuul:
routes:
order-service: /1/**
user-service: /2/**
省去了对服务名称的配置。
过滤器
Zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。
使用场景
场景非常多:
- 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
- 异常处理:一般会在error类型和post类型过滤器中结合来处理。
- 服务调用时长统计:pre和post结合使用。
自定义过滤器
接下来我们来自定义一个过滤器,模拟一个登录的校验。基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。
创建一个LoginFilter:
@Component
@Log4j2
public class LoginFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return -1;
}
@Override
public boolean shouldFilter() {
return true;
}
@SneakyThrows
@Override
public Object run() throws ZuulException {
log.warn("========================================");
//1)获取Zuul提供的请求上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
//2)从上下文中获取request对象
HttpServletRequest request = ctx.getRequest();
HttpServletResponse response = ctx.getResponse();
response.setContentType("application/json;charset=utf-8");
// 3) 从请求中获取token
//获取token
String token = request.getHeader("token");
log.info("token:{}",token);
String requestURI = request.getRequestURI();
if(requestURI.equals("/1/login")){
return null;
}
//判断token是否正常
if(StringUtils.isEmpty(token)){
Result result = new Result(401,"token丢失");
String json = new ObjectMapper().writeValueAsString(result);
// 没有token,登录校验失败,拦截
ctx.setSendZuulResponse(false);
// 返回401状态码(未授权)。也可以考虑重定向到登录页。
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
ctx.setResponseBody(json);
//拦截
//return false;
}
//处理token是否有效
boolean b = JwtUtil.checkToken(token);
if(!b){
Result result = new Result(401,"token失效");
String json = new ObjectMapper().writeValueAsString(result);
// 没有token,登录校验失败,拦截
ctx.setSendZuulResponse(false);
// 返回401状态码(未授权)。也可以考虑重定向到登录页。
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
ctx.setResponseBody(json);
//拦截
//return false;
}
return null;
}
}