SpringCloud系列教程记录02-Gateway路由网关

1.Gateway简介(Zuul增强版)

作用:api网关,路由,负载均衡等多种作用

简介:类似nginx,反向代理的功能,不过netflix自己增加了一些配合其他组件的特性。

在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。

2.maven需要的包

<properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

3.配置文件(application.yml)

spring:
  application:
    name: service-gateway
  profiles:
    active: dev
  servlet:
    multipart:
      max-file-size: 100MB
      maxRequestSize: 1000MB
  cloud:
    gateway:
      discovery:
        locator:
          lower-case-service-id: true
          enabled: true
server:
  port: 2000
  tomcat:
    basedir: /data/cloud/temp/

4.配置文件(application-dev.yml)

spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password: redis.6379
    jedis:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0

eureka:
  instance:
    prefer-ip-address: true
    instance-id: service-gateway
  client:
    service-url:
      defaultZone: http://localhost:1000/eureka/

5.注解(@EnableEurekaClient)

在这里插入图片描述

6.路由Token和权限拦截

@Component
public class TokenFilter implements GlobalFilter, Ordered {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final String AUTHORIZE_TOKEN = "token";
    private static final String[] EXCLUDE_TOKEN_PATHS = new String[]{
            "/service-auth/login"
    };

    private static final String[] EXCLUDE_AUTH_PATHS = new String[]{
            "/service-auth/logout",
            "/service-file/upload"
    };

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        logger.info("request:" + JSON.toJSONString(request));

        String path = request.getURI().getPath();
        //排除不需要token的地址
        if (Arrays.asList(EXCLUDE_TOKEN_PATHS).contains(path)) {
            return chain.filter(exchange);
        }

        MultiValueMap<String, String> queryParams = request.getQueryParams();
        //token校验
        String token = queryParams.getFirst(AUTHORIZE_TOKEN);
        ServerHttpResponse response = exchange.getResponse();

        if (StringUtils.isEmpty(token)) {
            logger.info("token不能为空");
            byte[] bytes = "{\"code\":\"401\",\"msg\":\"token不能为空\"}".getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        }

        String userJson = RedisUtil.getString(token);

        if (StringUtils.isBlank(userJson)) {
            logger.info("token无效,请重新登录");
            byte[] bytes = "{\"code\":\"401\",\"msg\":\"token无效,请重新登录\"}".getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        }

        //排除不需要token权限校验的地址
        if (Arrays.asList(EXCLUDE_AUTH_PATHS).contains(path)) {
            return chain.filter(exchange);
        }

        //权限校验
        Map<String, Object> userMap = JSON.parseObject(userJson, Map.class);
        List<String> permissionUrls = (List<String>) userMap.get("permissionUrls");
        if (!permissionUrls.contains(path.replaceAll("/service-\\w+/", "/"))) {
            logger.info("访问[" + path + "]权限不足");
            byte[] bytes = ("{\"code\":\"401\",\"msg\":\"访问[ " + path + "]权限不足\"}").getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        }

        RedisUtil.expire(token, 1, TimeUnit.HOURS);

        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.map(dataBuffer -> {
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        //释放掉内存
                        DataBufferUtils.release(dataBuffer);
                        String s = new String(content, Charset.forName("UTF-8"));
                        //就是response的值,想修改、查看就随意而为了
                        logger.info("response:" + s);
                        byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());

        //return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

}

7.封装过的RedisUtil

@Component
public class RedisUtil {
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    public static RedisUtil redisUtil;

    @PostConstruct
    public void init() {
        redisUtil = this;
    }

    /**
     * 设置redis字符串
     *
     * @param k        key
     * @param v        value
     * @param l        过期时间
     * @param timeUnit 时间类型
     */
    public static void setString(String k, String v, long l, TimeUnit timeUnit) {
        redisUtil.stringRedisTemplate.opsForValue().set(k, v, l, timeUnit);
    }

    /**
     * 设置redis字符串
     *
     * @param k key
     * @param v value
     */
    public static void setString(String k, String v) {
        redisUtil.stringRedisTemplate.opsForValue().set(k, v);
    }

    /**
     * 获取redis字符串
     *
     * @param k key
     * @return 返回键值
     */
    public static String getString(String k) {
        return redisUtil.stringRedisTemplate.opsForValue().get(k);
    }

    /**
     * 获取key的过期时间
     *
     * @param k key
     * @return
     */
    public static Long getExpire(String k) {
        return redisUtil.stringRedisTemplate.getExpire(k);
    }

    /**
     * 根据key获取过期时间并换算成指定单位
     *
     * @param k key
     * @return
     */
    public static Long getExpire(String k, TimeUnit timeUnit) {
        return redisUtil.stringRedisTemplate.getExpire(k, timeUnit);
    }

    /**
     * 根据key删除缓存
     *
     * @param k key
     */
    public static void delete(String k) {
        redisUtil.stringRedisTemplate.delete(k);
    }

    /**
     * 检查key是否存在
     *
     * @param k key
     * @return
     */
    public static boolean hasKey(String k) {
        return redisUtil.stringRedisTemplate.hasKey(k);
    }

    /**
     * 设置过期时间
     *
     * @param k        key
     * @param l        过期时间
     * @param timeUnit 过期时间单位
     * @return
     */
    public static boolean expire(String k, long l, TimeUnit timeUnit) {
        return redisUtil.stringRedisTemplate.expire(k, l, timeUnit);
    }

    /**
     * 设置指定时间过期
     *
     * @param k key
     * @param d date
     * @return
     */
    public static boolean expireAt(String k, Date d) {
        return redisUtil.stringRedisTemplate.expireAt(k, d);
    }

    /**
     * 将字符串添加到集合尾部
     *
     * @param k key
     * @param v value
     */
    public static void listPush(String k, String v) {
        redisUtil.stringRedisTemplate.opsForList().rightPush(k, v);
    }

    /**
     * 添加集合到redis
     *
     * @param k    key
     * @param list list
     */
    public static void listPushAll(String k, List<String> list) {
        redisUtil.stringRedisTemplate.opsForList().rightPushAll(k, list);
    }

    /**
     * 获取集合中单个元素
     *
     * @param k key
     * @param l index
     * @return
     */
    public static String listGet(String k, long l) {
        return redisUtil.stringRedisTemplate.opsForList().index(k, l);
    }

    /**
     * 获取集合的大小
     *
     * @param k key
     * @return
     */
    public static Long listSize(String k) {
        return redisUtil.stringRedisTemplate.opsForList().size(k);
    }

    /**
     * 获取集合所有元素
     *
     * @param k
     * @return
     */
    public static List<String> listAll(String k) {
        return redisUtil.stringRedisTemplate.opsForList().range(k, 0, -1);
    }
}

我写的这个网关主要是拦截一些不合法的请求和权限校验,利用了redis做缓存

发布了31 篇原创文章 · 获赞 15 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/dongdingzhuo/article/details/96975996