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


    新建Spring Boot项目

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

    pom依赖如下:

    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.5.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.lifengdi</groupId>
        <artifactId>gateway</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>gateway</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
    
    <!--        <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
            </dependency>-->
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
            </dependency>
    
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.5</version>
                <scope>compile</scope>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.58</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-mail</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>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    由于是网关项目,所以不需要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包如下:

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

    默认的熔断回调接口:

    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<Object> 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包:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    

    相关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<ResponseCookie.ResponseCookieBuilder> 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}
     * <p>
     * 使用EnableRedisHttpSession时{@link DefaultCookieSerializer}中useBase64Encoding默认为true,将cookie中的sessionId使用base64
     * 加密,但是如果使用{@link org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession},默认
     * 的解析器没有将sessionId解密,导致获取不到正确的session
     * </p>
     *
     * @author: Li Fengdi
     * @date: 2020/3/16 15:41
     */
    @Slf4j
    public class MyCookieWebSessionIdResolver extends CookieWebSessionIdResolver {
    
        @Override
        public List<String> resolveSessionIds(ServerWebExchange exchange) {
            MultiValueMap<String, HttpCookie> cookieMap = exchange.getRequest().getCookies();
            List<HttpCookie> 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

    原文地址:https://www.lifengdi.com/archives/article/1776

  • 相关阅读:
    P4149 [IOI2011]Race dsu on tree
    CodeForces
    2020牛客国庆集训派对day2 CHEAP DELIVERIES
    Hero In Maze
    topo排序
    今年暑假不AC
    小国的游戏
    Stall Reservations
    博弈论
    Reversed Words
  • 原文地址:https://www.cnblogs.com/lifengdi/p/12519344.html
Copyright © 2020-2023  润新知