Spring-Cloud-zuul服务网关

一. 什么是API网关

	在微服务架构中,通常会有多个服务提供者。设想一个电商系统,可能会有商品、订单、支付、用户等多个类型的服务,
而每个类型的服务数量也会随着整个系统体量的增大也会随之增长和变更。作为UI端,在展示页面时可能需要从多个微服务中聚合数据,
而且服务的划分位置结构可能会有所改变。网关就可以对外暴露聚合API,屏蔽内部微服务的微小变动,保持整个系统的稳定性。
当然这只是网关众多功能中的一部分,它还可以做负载均衡,统一鉴权,协议转换,监控监测等一系列功能

在这里插入图片描述

二.什么是zuul?

Zuul是Spring Cloud全家桶中的微服务API网关。
所有从设备或网站来的请求都会经过Zuul到达后端的Netflix应用程序。作为一个边界性质的应用程序,
Zuul提供了动态路由、监控、弹性负载和安全功能。
Zuul底层利用各种filter实现如下功能:
		认证和安全 识别每个需要认证的资源,拒绝不符合要求的请求。
		性能监测 在服务边界追踪并统计数据,提供精确的生产视图。
		动态路由 根据需要将请求动态路由到后端集群。
		压力测试 逐渐增加对集群的流量以了解其性能。
		负载卸载 预先为每种类型的请求分配容量,当请求超过容量时自动丢弃。
		静态资源处理 直接在边界返回某些响应。

三.搭建环境

pom.xml 配置如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
        <version>2.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        <version>2.1.1.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>com.jwxt</groupId>
        <artifactId>jwxt-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.jwxt</groupId>
        <artifactId>jwxt-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
        <scope>compile</scope>
    </dependency>

</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

application,yml 配置如下:

server:
  port: 56700
spring:
  application:
    name: my-zuul

  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/kytms?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    #url: jdbc:mysql://192.168.5.240:3306/kytms?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root

  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
    timeout: 50000

eureka:
  client:
    service-url:
      defaultZone: http://localhost:56798/eureka/,http://localhost:56799/eureka/
  instance:
    prefer-ip-address: true
    instance-id: my_zuul8088
#zuul:       也是配置路由访问的一种方式
#  routes:
#    my-system: /system/**
zuul:
  routes:
    jwxt-teacher:
      path: /teacher/**  #访问的路径
      service-id: jwxt-teacher   #访问的服务
    jwxt-learner:
      path: /student/**
      service-id: jwxt-learner
  ignored-services: '*'
  #ignored-patterns: /**/findOne/**   #忽略带有findOne的请求(就不能再访问带有findOne的请求)
  #prefix: /api     #访问路径的前缀,需要多加一层的/api

ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000
  eureka:
    enabled: true

创建一个 拦截器类:

package com.jwxt.filter;
import com.alibaba.fastjson.JSON;
import com.jwxt.response.Result;
import com.jwxt.response.ResultCode;
import com.jwxt.utils.JwtUtils;
import com.jwxt.utils.RedisUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.constants.ZuulConstants;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyZuulFilter extends ZuulFilter {

@Autowired
private RedisTemplate<String,Object> redisTemplate;


@Override
public String filterType() {
    return FilterConstants.PRE_TYPE;
}

@Override
public int filterOrder() {
    return 1;
}

@Override
public boolean shouldFilter() {
    return true;
}

@Override
public Object run() throws ZuulException {

    RequestContext currentContext = RequestContext.getCurrentContext();
    HttpServletRequest request = currentContext.getRequest();
    HttpServletResponse response = currentContext.getResponse();

    StringBuffer requestURL = request.getRequestURL();

    if (requestURL.toString().contains("teacher/login")) return null;

    //String authorization = request.getHeader("Authorization");
    String authorization = request.getParameter("token");
    if (!StringUtils.isEmpty(authorization) && authorization.startsWith("Bearer")) {
        String token = authorization.replace("Bearer ","");
        Claims claims = JwtUtils.parseJwt(token);

        if (claims == null) {
            access_denied();
            return null;
        }else{
            redisTemplate.opsForValue().set("user_claims",claims);
           /* if (redisTemplate.hasKey(claims.getId())) {
                access_denied();
                return null;
            }*/
            return null;
        }


        /*
        Cookie cookie = ServletUtil.getCookie(request, "token_key");

        if (cookie == null) {
            access_denied();
            return null;
        }*/

    } else {
        access_denied();
        return null;
    }
}

//拒绝访问
protected void access_denied(){
    RequestContext requestContext = RequestContext.getCurrentContext();
    //得到response
    HttpServletResponse response = requestContext.getResponse();
    //拒绝访问
    requestContext.setSendZuulResponse(false);
    //设置响应代码
    requestContext.setResponseStatusCode(200);
    //构建响应的信息
    Result responseResult = new Result(ResultCode.NO_EXIST_PERMISSION);
    //转成json
    String jsonString = JSON.toJSONString(responseResult);
    requestContext.setResponseBody(jsonString);
    //转成json,设置contentType
    response.setContentType("application/json;charset=utf-8");
}

}

跨域设置: controller层不需要再加@CrossOrigin注解

package com.jwxt;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;

/**
 * 跨域配置
 */
@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();

        config.setAllowCredentials(true);
        config.setAllowedOrigins(Arrays.asList("*")); //http:www.a.com
        config.setAllowedHeaders(Arrays.asList("*"));
        config.setAllowedMethods(Arrays.asList("*"));
        config.setMaxAge(300l);

        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

最后配置启动类:

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class MyZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyZuulApplication.class,args);
    }
}

(注意: 启动流程:先启动服务方-再启动zuul网关-再启动消费方)


版权声明:本文为weixin_43982839原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。