zuul
基于Spring的微服务结点在能力上没有高低贵贱之分,但是在角色上会分为边缘服务和内部服务两部分。内部服务顾名思义是为对内暴露服务的结点,供架构内部来调用;边缘服务是对外部网络暴露的服务结点,也就是对外API接口。
开发人员头疼的地方:为了防止我的程序在网络上被人攻击,我们需要写各种权限机制,这些机制在每个微服务结点都要实现一次。一旦鉴权上有什么bug,又要全部节点上推倒重来,噩梦。
运维人员头疼的地方:边缘服务前段都会架一个F5或者Nginx等负载均衡的代理,需要手动维护一份服务列表和服务地址的路由信息,随着结点的扩展或地址调整这份列表要变来变去。
为了解决鉴权重复的问题,使业务结点本身只关心实现自己的业务,将对权限的处理抽离到上层。外部客户先请求到Zuul上,在Zuul服务上对权限进行统一实现和过滤,以实现微服务结点的过滤和验证。
为了解决请求路由和安全过滤,Spring Cloud推出了一个API gateway组件:Spring Cloud Zuul。
在路由方面,Zuul将自己作为一个微服务结点注册到Eureka上,就获取了所有微服务的实例信息,同时又以服务名为ContextPath的方式创建路由映射。
创建工程 eureka-zuul
在parent-clould 项目上新建一个module, 取名eureka-zuul
pom.xml文件内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.could.shj</groupId>
<artifactId>eureka-zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>eureka-zuul</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.could.shj</groupId>
<artifactId>parent-clould</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
</project>
启动类如下
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableZuulProxy
public class EurekaZuulApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaZuulApplication.class, args);
}
}
配置文件如下
server.port=8787
eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/
spring.application.name=eureka-zuul
zuul.routes.ribbon.path=/ribbon/**
zuul.routes.ribbon.serviceId=eureka-ribbon
zuul.routes.feign.path=/feign/**
zuul.routes.feign.serviceId=eureka-feign
服务为eureka-zuul,端口为8787,以 /ribbon/开头的请求转发给eureka-ribbon
以/feign/ 开头的请求,转发给 erueka-feign
依次启动,server,client,ribbon,feign,zuul,在浏览器中输入:
http://127.0.0.1:8787/ribbon/hello?name=shj
http://127.0.0.1:8787/feign/hello?name=shj
注意: 刚开始的时候可能会返回一个异常页面,刷新网页就好
此时注册的服务有:
zuul过滤
写一个过滤类
package com.could.shj.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* @version 1.0
* @className MyFilter
* @Description
* @Author shj
* @Date 2018/7/27、14:19
*/
@Component
public class MyFilter extends ZuulFilter{
@Override
public String filterType() {
/*filterType 返回一个字符串代表过滤的类型:
可返回的内容及其含义
pre:路由之前
routing:路由之时
post:路由之后
error:发送错误调用
*/
return "pre";
}
@Override
public int filterOrder() {
//过滤的顺序
return 0;
}
@Override
public boolean shouldFilter() {
// 可以写逻辑判断, 是否要过滤,true表示需要过滤
return true;
}
@Override
public Object run() throws ZuulException {
// 过滤的具体内容
// 可以很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。
RequestContext requestContext=RequestContext.getCurrentContext();
HttpServletRequest request=requestContext.getRequest();
Object token=request.getParameter("token");
if(token==null){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(401);
try {
requestContext.getResponse().getWriter().write("token 为空");
}catch (Exception e){
return null;
}
}
return null;
}
}
然后在浏览器中访问
http://127.0.0.1:8787/ribbon/hello?name=shj
http://127.0.0.1:8787/ribbon/hello?name=shj&token=1
注意: 此时可能会访问到error page ,刷新页面就好
完成后项目的路径为: