SpringCloud集成Zuul服务网关

摘要:

现有很多微服务模块,多个模块都需要做登录校验,如果每个模块都单独写一套登录检查逻辑,这样代码量高、耦合度也高,非常不利于来发,我们需要将登录状态验证的逻辑抽取出来,而Zuul就是做这个事的,它本质上是一个WebServlet,是一个服务网关,是访问所有微服务的大门,我们在项目上线的时候只会将Zuul这一个微服务暴露在外网中,而其他的服务都是部署在内网中

一:引入依赖

<dependencies>
    <!--引入Eureka的客户端依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--引入springboot-start-web依赖,必须引入-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--Zull依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
</dependencies>

 二:启动类上打上注解@EnableZuulProxy开启Zuul支持

三:配置application.yml

server:
    port: 10005 #user服务端口号

eureka:
    client: #Eureka客户端配置,指向注册中心地址
        serviceUrl:
            defaultZone:http://localhost:10001/eureka/
    instance:
        prefer-ip-address:true #开启使用IP地址进行注册
        instance-id:ZuulServer:10005 #修改实例Id
spring:
    application: #指定此服务的应用名称
        name:ZuulServer

zuul:
    prefix:"/apis"#统一访问前缀
    ignoredServices:"*"#禁用掉使用浏览器通过服务名的方式访问服务
    routes:
        pay-server:"/pay/**"#指定pay-server这个服务使用/pay路径来访问-别名
        order-server:"/order/**"#指定order-server这个服务使用/order路径来访问

四:发送请求的说明

在没有使用Zuul之前,我们可以通过微服务的端口加接口的地址进行访问,而现在我们需要通过zuul来访问,即:http://localhost:10005/apis/目标服务的接口地址

五:Zuul自定义前置拦截器案例

package cn.ybl.filters;

import com.alibaba.fastjson.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;

/**
	 * 自定义Zuul拦截器
	 */
@Component
public class LoginCheckFilter extends ZuulFilter {

    //用来指定filter的类型的,例如返回pre那么就是前置filter
    @Override
    public String filterType() {
        return "pre";
    }

    //filter的执行顺序,越小越先执行
    @Override
    public int filterOrder() {
        return 0;
    }

    //父接口为IZuulFilter的方法,返回值true就执行run方法
    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext(); //获取上下文对象
        String uri = currentContext.getRequest().getRequestURI(); //得到当前请求的uri
        //判断当前的uri是否包含login或者register,包含就不做校验返回false,否则就要校验
        if(StringUtils.endsWithIgnoreCase(uri,"login") || StringUtils.endsWithIgnoreCase(uri,"register")){
            return false;
        }
        return true;
    }

    //父接口为IZuulFilter的方法,该方法是Filter的核心业务方法
    @Override
    public Object run() throws ZuulException {
        try {
            //从请求头获取token
            String token = RequestContext.getCurrentContext().getRequest().getHeader("token");
            //获取响应体
            HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
            response.setContentType("application/json;Charset=UTF-8");
            if(StringUtils.isEmpty(token)) {
                HashMap<Object, Object> map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "nologin");
                PrintWriter writer = response.getWriter();
                //将map转为json字符串返回给浏览器,提示未登录,也可以手动拼接
                writer.println(JSONObject.parse(JSONObject.toJSONString(map)));
                //阻止后续拦截器执行
                RequestContext.getCurrentContext().setSendZuulResponse(false);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 如果要放行,这里返回null即可,不用管
        return null;
    }
}

六:Zuul熔断器配置案例

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;


@Component
public class PayServerFallback implements FallbackProvider {

    // 得到日志对象
    private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);

    // 指定要处理的服务。
    @Override
    public String getRoute() {
        return "pay-server";  //"*"代表所有服务都有作用
    }

    /**
	     * @param route :服务的路由
	     * @param cause : 异常
	     * @return ClientHttpResponse:熔断后的换回值
	     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("抱歉,服务不可用".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}
posted @ 2022-08-22 13:22  yyybl  阅读(24)  评论(0)    收藏  举报