从零搭建Spring Cloud Gateway网关(一)

2020年3月18日 633点热度 0人点赞 0条评论

新建Spring Boot项目

怎么新建Spring Boot项目这里不再具体赘述,不会的可以翻看下之前的博客或者直接百度。这里直接贴出对应的pom文件。

pom依赖如下:

4.0.0org.springframework.bootspring-boot-starter-parent2.2.5.RELEASEcom.lifengdigateway0.0.1-SNAPSHOTgatewayDemo project for Spring Boot1.8Hoxton.SR3org.springframework.cloudspring-cloud-starter-gatewayorg.projectlomboklombok1.18.12trueorg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-engineorg.springframework.cloudspring-cloud-starter-netflix-hystrixorg.springframework.bootspring-boot-starter-actuatororg.apache.commonscommons-pool2commons-iocommons-io2.5compilecom.alibabafastjson1.2.58org.springframework.bootspring-boot-starter-mailorg.springframework.cloudspring-cloud-dependencies${spring-cloud.version}pomimportorg.springframework.bootspring-boot-maven-plugin

由于是网关项目,所以不需要spring-boot-starter-web相关的依赖。

配置文件如下:

server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway-demo
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用路由访问
      routes:
        - id: path_route
          # 指定域名
          uri: http://localhost:8081
          predicates:
            - Path=/jar/**
          filters:
            # 熔断配置
            - name: Hystrix
              args:
                name: default
                fallbackUri: forward:/fallback
        - id: path_route2
          # 指定域名
          uri: http://localhost:8082
          predicates:
            - Path=/war/**
          filters:
            # 熔断配置
            - name: Hystrix
              args:
                name: hystrix1
                fallbackUri: forward:/fallback

  mvc:
    throw-exception-if-no-handler-found: true

# 默认熔断超时时间30s
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
    hystrix1:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000

熔断(接口或者项目)

熔断相关jar包如下:

org.springframework.cloudspring-cloud-starter-netflix-hystrix

默认的熔断回调接口:

package com.lifengdi.gateway.hystrix;

import com.lifengdi.gateway.exception.BaseException;
import com.lifengdi.gateway.response.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author: Li Fengdi
 * @date: 2020-03-18 16:35
 */
@RestController
@Slf4j
public class DefaultHystrixController {
    @RequestMapping("/fallback")
    public ResponseResult fallback(){

        log.error("触发熔断......");
        return ResponseResult.fail(BaseException.DEFAULT_HYSTRIX.build());
    }
}

具体配置文件说明如下:

      routes:
        - id: path_route
          # 指定域名
          uri: http://localhost:8081
          predicates:
            - Path=/jar/**
          filters:
            # 熔断配置
            - name: Hystrix
              args:
                name: default
                fallbackUri: forward:/fallback
        - id: path_route2
          # 指定域名
          uri: http://localhost:8082
          predicates:
            - Path=/war/**
          filters:
            # 熔断配置
            - name: Hystrix
              args:
                name: hystrix1
                fallbackUri: forward:/fallback

  mvc:
    throw-exception-if-no-handler-found: true

# 默认熔断超时时间30s
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
    hystrix1:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000

defaulthystrix1为自定义的参数,可以配置多个熔断策略,不同的接口、服务可以单独配置对应的超时时间,不需要额外的进行开发,不过需要增加额外的配置文件。

全局session共享

依赖jar包:

org.springframework.bootspring-boot-starter-data-redis-reactiveorg.springframework.sessionspring-session-data-redisorg.apache.commonscommons-pool2org.springframework.bootspring-boot-starter-security

相关yml配置:

spring:
  redis:
    database: 0
    host: localhost
    port: 6379
    password: 123456
    lettuce:
      pool:
        max-active: 300
        max-idle: 8
        max-wait: -1ms
        min-idle: 0
  session:
    store-type: redis

spring.session.store-typeSpring默认就是redis实现的,也有其他的,配置不同罢了。

增加代码如下:

权限相关,这里默认全部放行:

package com.lifengdi.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration
@EnableWebFluxSecurity
public class GatewaySecurityConfig {
    @Bean
    SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity serverHttpSecurity)
            throws Exception {
        serverHttpSecurity
                .csrf().disable()
                .authorizeExchange().pathMatchers("/**").permitAll()
                .anyExchange()
                .authenticated();
        return serverHttpSecurity.build();
    }
}

session相关:

package com.lifengdi.gateway.config;

import com.lifengdi.gateway.resolver.MyCookieWebSessionIdResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseCookie;
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
import org.springframework.web.server.session.CookieWebSessionIdResolver;
import org.springframework.web.server.session.WebSessionIdResolver;

import java.util.function.Consumer;

@Configuration
@EnableRedisWebSession(maxInactiveIntervalInSeconds = 10*60*60, redisNamespace = "my:spring:session")
public class WebSessionConfig {

    @Bean
    public WebSessionIdResolver webSessionIdResolver() {
        CookieWebSessionIdResolver resolver = new MyCookieWebSessionIdResolver();
        resolver.setCookieName("SESSIONID");

        Consumer consumer = responseCookieBuilder -> {
            responseCookieBuilder.path("/");
        };
        resolver.addCookieInitializer(consumer);
        return resolver;
    }

}

注意这里使用的是@EnableRedisWebSession注解,而不是@EnableRedisHttpSession,这个是和zuul不一样的地方。

用zuul做网关的时候,直接使用@EnableRedisHttpSession在配置里面就可以通过redis共享session信息

Spring同时提供了@EnableRedisWebSession来对WebFlux的支持。

值得一提的是这两个注解内部实现并不相同,需要自定义的配置也不一样。

这里自定义cookieName、path等是自定义了webSessionIdResolver来实现的,而不是cookieSerializer。如果使用cookieSerializer的话,对@EnableRedisWebSession来说是不起作用的。这个坑之前坑了好半天!

MyCookieWebSessionIdResolver代码如下:

package com.lifengdi.gateway.resolver;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpCookie;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.session.CookieWebSessionIdResolver;

import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 自定义WebSessionId解析器,以兼容{@link org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession}
 * 

* 使用EnableRedisHttpSession时{@link DefaultCookieSerializer}中useBase64Encoding默认为true,将cookie中的sessionId使用base64 * 加密,但是如果使用{@link org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession},默认 * 的解析器没有将sessionId解密,导致获取不到正确的session *

* * @author: Li Fengdi * @date: 2020/3/16 15:41 */ @Slf4j public class MyCookieWebSessionIdResolver extends CookieWebSessionIdResolver { @Override public List resolveSessionIds(ServerWebExchange exchange) { MultiValueMap cookieMap = exchange.getRequest().getCookies(); List cookies = cookieMap.get(getCookieName()); if (cookies == null) { return Collections.emptyList(); } return cookies.stream().map(HttpCookie::getValue).map(this::base64Decode).collect(Collectors.toList()); } /** * base64解码 * * @param base64Value base64Value * @return 解码后的字符串 */ private String base64Decode(String base64Value) { try { byte[] decodedCookieBytes = Base64.getDecoder().decode(base64Value); return new String(decodedCookieBytes); } catch (Exception ex) { log.debug("Unable to Base64 decode value: " + base64Value); return null; } } }

其实这段代码本就是参考了cookieSerializer中的代码来实现的。

如果指定了useBase64Encoding为false,即不加密sessionId,那么就不需要这一段代码了。

代码已上传到git上,需要的可以去看看。

git代码地址:https://github.com/lifengdi/spring-cloud-gateway-demo

除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

李锋镝

既然选择了远方,便只顾风雨兼程。

guest
0 评论
Inline Feedbacks
查看所有评论