• springcloud-网关(gateway)


    springcloud-网关(gateway)

    概述

    \Spring Cloud Gateway旨在提供一种简单而有效的方式来路由到API,并为其提供跨领域的关注,如:安全、监控/指标和容错

    常用术语

    • Route(路由): 网关的基本构件。它由一个ID、一个目的地URI、一个谓词(Predicate)集合和一个过滤器(Filter)集合定义。如果集合谓词为真,则路由被匹配。
    • Predicate(谓词): 这是一个 Java 8 Function Predicate。输入类型是 [Spring Framework ServerWebExchange]。这让你可以在HTTP请求中的任何内容上进行匹配,比如header或查询参数。
    • Filter(过滤器): 这些是 [GatewayFilter] 的实例,已经用特定工厂构建。在这里,你可以在发送下游请求之前或之后修改请求和响应。

    网关工作原理

    客户端向 Spring Cloud Gateway 发出请求。如果Gateway处理程序映射确定一个请求与路由相匹配,它将被发送到Gateway Web处理程序。这个处理程序通过一个特定于该请求的过滤器链来运行该请求。过滤器被分割的原因是,过滤器可以在代理请求发送之前和之后运行逻辑。所有的 "pre" (前)过滤器逻辑都被执行。然后发出代理请求。在代理请求发出后,"post" (后)过滤器逻辑被运行。

    image-20231213121300712

    业务架构图

    Spring Cloud:Feign负载均衡及熔断&Gateway网关的路由及过滤器&Gateway跨域&Spring Cloud Config分布式配置中心&Spring Cloud Bus ...

    开发流程

    引入依赖

    gateway框架需要引入loadbalancer,否则在访问的时候会出现503异常。

    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-loadbalancerartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-gatewayartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    bootstrap.yml配置

    配置nacos

    如果不需要nacos的配置,可以不引入nacos的config依赖

    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    spring:
      application:
        name: ssc-cloud-gateway
      cloud:
        nacos:
          discovery:
            server-addr: 192.168.201.81:7777
            namespace: ssc-cloud-id
            group: DEV
            register-enabled: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    配置gateway
    spring:
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true
          routes:
            - id : ssc-order-id               #路由编号(自定义)
    #          uri: https://www.bilibili.com/   #跳转地址(网页跳转),lb--负载调整(微服务)
              uri: lb://ssc-cloud-older
              predicates:
                - Path=/ssc-older/**       #url 的映射名称(让客户看到的)
              filters:
                - StripPrefix=1
            - id: ssc-paycenter-id               #路由编号(自定义)
              uri: lb://ssc-cloud-paycenter
              predicates:
                - Path=/ssc-pay/**
              filters:
                - StripPrefix=1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    路由配置规则
    序号名称解释
    1id⾃定义的路由 ID,保持唯⼀
    2uri目标服务地址:以lb:// 开头则表示要去向的微服务名称
    3predicates路由条件
    4Filter(过滤器)过滤器是路由转发请求时所经过的过滤逻辑,可⽤于修改请求、响应内容。
    predicates断言条件
    • Path

      实例: - Path=/ssc-older/

      routes:
          - id : ssc-older-id          
            uri: lb://ssc-cloud-older
            predicates:
              - Path=/ssc-older/**    
      
      • 1
      • 2
      • 3
      • 4
      • 5

      解释: 当请求的路径为ssc-older开头的时,转发到ssc-cloud-older微服服务器上,ssc-cloud-older是navcos中微服务注册的名称。

      image-20231213175938386

    • Before

      - Before=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]

      在某个时间之前的请求才会被转发到 http://localhost:9001 服务器上

    • After

      - After=2017-01-20T17:42:47.789- 07:00[Asia/Shanghai]

      在某个时间之后的请求才会被转发

    • Between

      - Between=2017-01-20T17:42:47.789-07:00[Asia/Shanghai],

      2017-01- 21T17:42:47.789-07:00[Asia/Shanghai]

      在某个时间段之间的才会被转发

    • Cookie

      - Cookie=sesionId,ssc-test

      名为ss-old的表单或者满⾜正则old的表单才会被匹配到 进⾏请求转发

      routes:
          - id : ssc-older-id          
            uri: lb://ssc-cloud-older
            predicates:
              - Cookie=sesionId,ssc-test   
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • Header

      - Header=X-Request-Id

      携带参数X-Request-Id的请求头才会匹配

    • Host

      - Host=www.ssc.older.com

      当主机名为www.ssc.older.com的时候直接转发到指定服务器。

    • Method

      - Method=GET

      只有GET⽅法才会匹配转发请求,还可以限定POST、PUT等。

      routes:
          - id : ssc-older-id          
            uri: lb://ssc-cloud-older
            predicates:
              - Method=GET,POST  
      
      • 1
      • 2
      • 3
      • 4
      • 5
    过滤器规则
    过滤规则实例说明
    PrefixPath- PrefixPath=/app在请求路径前加上app
    PrefixPath- PrefixPath=/app在请求路径前加上app
    SetPathSetPath=/app/{path}通过模板设置路径,转发的规则时会在路径前增加 app,{path}表示原请求路径
    RedirectTo重定向
    RemoveRequestHea去掉某个请求头信息\1. PrefixPath
    StripPrefixPath=/ssc-pay/**去掉ssc-pay才是真实路径

    网关的案例

    使用网关进行用户鉴权过滤。

    image-20231220095437516

    鉴权微服务

    image-20231220095804335

    api模块

    api模块为外界提供接口(openfeign调用),为网关调用需要基于reactor开发。

    pom
    
    <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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>com.wnhz.ssc.cloud.authoritygroupId>
            <artifactId>ssc-cloud-authorityartifactId>
            <version>0.0.1-SNAPSHOTversion>
        parent>
    
        <groupId>com.wnhz.ssc.cloud.authority.apigroupId>
        <artifactId>ssc-cloud-authority-apiartifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.playtika.reactivefeigngroupId>
                <artifactId>feign-reactor-webclientartifactId>
                <version>3.0.3version>
            dependency>
    
            <dependency>
                <groupId>com.playtika.reactivefeigngroupId>
                <artifactId>feign-reactor-cloudartifactId>
                <version>3.0.3version>
            dependency>
    
            <dependency>
                <groupId>com.playtika.reactivefeigngroupId>
                <artifactId>feign-reactor-spring-configurationartifactId>
                <version>3.0.3version>
            dependency>
        dependencies>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    openfeign接口
    @ReactiveFeignClient(value = "ssc-cloud-authority",url = "http://localhost:12121")
    public interface IAuthorityFeign {
    
        @GetMapping("/api/authority/login")
        Mono<HttpResp> login(@RequestParam("username") String username,
                                  @RequestParam("password") String password);
    
        @PostMapping("/api/authority/verifyToken")
        Mono<HttpResp> verifyToken(String token);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    业务接口(sevice)

    pom.xml
    
    <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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>com.wnhz.ssc.cloud.authoritygroupId>
            <artifactId>ssc-cloud-authorityartifactId>
            <version>0.0.1-SNAPSHOTversion>
        parent>
    
        <groupId>com.wnhz.ssc.cloud.authority.servicegroupId>
        <artifactId>ssc-cloud-authority-serviceartifactId>
    
        <dependencies>
    
            <dependency>
                <groupId>com.wnhz.ssc.cloud.springcloudgroupId>
                <artifactId>ssc-cloud-springcloudartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
    
            <dependency>
                <groupId>com.github.xiaoymingroupId>
                <artifactId>knife4j-openapi2-spring-boot-starterartifactId>
            dependency>
            <dependency>
                <groupId>com.wnhz.ssc.cloudgroupId>
                <artifactId>ssc-cloud-toolsartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
            <dependency>
                <groupId>com.wnhz.ssc.cloudgroupId>
                <artifactId>ssc-cloud-ssmartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-redisartifactId>
            dependency>
    
        dependencies>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    配置

    image-20231220100840317

    bootstrap.yml

    image-20231220100632172

    image-20231220100711263

    spring:
      cloud:
        nacos:
          config:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            namespace: ${spring.cloud.nacos.discovery.namespace}
            file-extension: yaml
            extension-configs:
              - data-id: db_ssc.yaml
                group: DEV
                refresh: true
              - data-id: redis.yaml
                group: DEV
                refresh: true
          discovery:
            server-addr: 192.168.201.107
            namespace: ssc-cloud-id
            group: DEV
      application:
        name: ssc-cloud-authority
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    application
    • application-dev.yml

      server:
        port: 12121
      knife4j:
        enable: true
      logging:
        level:
          com.wnhz: debug
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • application.yml

      spring:
        profiles:
          active: dev
      
      • 1
      • 2
      • 3
    启动类
    @EnableDiscoveryClient
    @SpringBootApplication
    public class AuthorityApp {
        public static void main(String[] args) {
            SpringApplication.run(AuthorityApp.class);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    dao
    @Mapper
    public interface ILoginDao extends BaseMapper<Login> {
    }
    
    • 1
    • 2
    • 3
    异常

    image-20231220101139895

    /**
     * 鉴权异常
     */
    public class AuthorityException extends RuntimeException{
        public AuthorityException(String message) {
            super(message);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    /**
     * 用户名或密码错误异常
     */
    public class BadCredentialsException extends AuthorityException{
        public BadCredentialsException(String message) {
            super(message);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    service
    • 接口

      public interface ILoginService {
          Login loginCheck(String username, String password);
          List<Login> findByUsername(String username);
      }
      
      • 1
      • 2
      • 3
      • 4
    • 实现类

      package com.wnhz.ssc.cloud.authority.service.impl;
      
      import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
      import com.wnhz.ssc.cloud.authority.dao.ILoginDao;
      import com.wnhz.ssc.cloud.authority.exception.BadCredentialsException;
      import com.wnhz.ssc.cloud.authority.exception.UsernameEmptyException;
      import com.wnhz.ssc.cloud.authority.exception.UsernameNotFoundException;
      import com.wnhz.ssc.cloud.authority.service.ILoginService;
      import com.wnhz.ssc.domain.entity.po.Login;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      import org.springframework.util.StringUtils;
      
      import java.util.List;
      import java.util.Objects;
      
      @Service
      public class LoginServiceImpl implements ILoginService {
          @Autowired
          private ILoginDao loginDao;
      
          @Override
          public Login loginCheck(String username, String password) {
              if (!StringUtils.hasText(username)) throw new UsernameEmptyException("用户名为空异常");
              LambdaQueryWrapper<Login> lambdaQueryWrapper
                      = new LambdaQueryWrapper<>();
              lambdaQueryWrapper.eq(Login::getUsername, username);
              List<Login> logins = findByUsername(username);
              Login login = logins.stream()
                      .filter(lg -> username.equals(lg.getUsername())).findFirst().get();
              if (Objects.isNull(login)) throw new BadCredentialsException("用户名|密码错误");
              return login;
          }
      
          /**
           * 判读用户名是否存在
           * @param username 用户名
           * @return 所有查询服务用户名的用户集合
           */
          @Override
          public List<Login> findByUsername(String username) {
              List<Login> logins = loginDao.selectList(new LambdaQueryWrapper<Login>().eq(Login::getUsername, username));
              if (Objects.isNull(logins) || logins.isEmpty()) throw new UsernameNotFoundException("用户名不存在异常");
              return logins;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
    controller
    package com.wnhz.ssc.cloud.authority.controller;
    
    import com.wnhz.ssc.cloud.authority.service.ILoginService;
    import com.wnhz.ssc.cloud.tools.exception.jwt.JwtException;
    import com.wnhz.ssc.cloud.tools.exception.jwt.JwtExpiredException;
    import com.wnhz.ssc.cloud.tools.jwt.JwtUtil;
    import com.wnhz.ssc.common.result.HttpResp;
    import com.wnhz.ssc.domain.entity.po.Login;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import reactor.core.publisher.Mono;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Objects;
    import java.util.concurrent.TimeUnit;
    
    @Api(tags = "鉴权模块api")
    @RestController
    @RequestMapping("/api/authority")
    @Slf4j
    public class AuthorityController {
    
        @Autowired
        private ILoginService loginService;
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
        private final String REDIS_PREFIX="token:";
    
        /**
         * 用户登录
         *
         * @param username
         * @param password
         * @return
         */
        @ApiOperation(value = "login", notes = "用户登录验证")
        @GetMapping("/login")
        public HttpResp login(String username, String password) {
            try {
                Login login = loginService.loginCheck(username, password);
                log.debug("用户名登录成功:{}",login);
                Map<String, Object> map = new HashMap<>();
                map.put("username", username);
                String token = JwtUtil.getInstance().creatToken(
                        map, 1000 * 30, login.getSign());
                stringRedisTemplate.opsForValue().set(REDIS_PREFIX+token, login.getSign(),
                        10, TimeUnit.MINUTES);
                log.debug("token产生成功,并存入redis中:{}",token);
                return HttpResp.success(new String[]{"ssc_login_token", token});
            } catch (Exception e) {
                return HttpResp.failed(e.getMessage());
            }
        }
    
        /**
         * 验证token
         *
         * @param token
         * @return
         */
        @ApiOperation(value = "verifyToken", notes = "token验证")
        @GetMapping("/verifyToken")
        public Mono<HttpResp> verifyToken(String token) {
            String sign = stringRedisTemplate.opsForValue().get(REDIS_PREFIX+token);
            log.debug("从redis中获取token的值: {}", sign);
            if (Objects.isNull(sign)) {
                return Mono.just(HttpResp.failed("token不存在"));
            }
            try {
                JwtUtil.getInstance().verifyToken(token, sign);
                return Mono.just(HttpResp.success("token验证成功"));
            } catch (JwtExpiredException e) {//token已过期,判断是否续期
                return refreshToken(token, sign);
            } catch (JwtException e) {
                return Mono.just(HttpResp.failed(e.getMessage()));
            }
        }
    
        private Mono<HttpResp> refreshToken(String originalToken, String sign) {
            Long expireLeftTime = stringRedisTemplate.getExpire(REDIS_PREFIX+originalToken);
            log.debug("redis中token剩余时间:{}", expireLeftTime);
            if (expireLeftTime > 0) {//token续期
                String newToken = JwtUtil.getInstance().cloneToken(originalToken, expireLeftTime);
                stringRedisTemplate.delete(REDIS_PREFIX+originalToken);
                stringRedisTemplate.opsForValue().set(REDIS_PREFIX+newToken, sign);
                log.debug("新token产生成功,续期完成,token==> {}",newToken);
                return Mono.just(HttpResp.success(new String[]{"ssc_login_token", newToken}));
            } //token已经过期,redis也过期
            return Mono.just(HttpResp.failed("token已经过期"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    全局网关

    负责对鉴权控制

    pom

    
    <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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>wnhz-ssc-cloudartifactId>
            <groupId>com.wnhz.ssc.cloudgroupId>
            <version>0.0.1-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <groupId>com.wnhz.ssc.cloud.gatewaygroupId>
        <artifactId>ssc-cloud-gatewayartifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.wnhz.ssc.cloud.springcloudgroupId>
                <artifactId>ssc-cloud-springcloudartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
            <dependency>
                <groupId>com.wnhz.ssc.cloud.commongroupId>
                <artifactId>ssc-cloud-commonartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-gatewayartifactId>
            dependency>
            <dependency>
                <groupId>com.playtika.reactivefeigngroupId>
                <artifactId>feign-reactor-spring-configurationartifactId>
                <version>3.0.3version>
                <scope>compilescope>
            dependency>
    
            <dependency>
                <groupId>com.wnhz.ssc.cloud.authority.apigroupId>
                <artifactId>ssc-cloud-authority-apiartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
    
    
        dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                    <configuration>
                        <outputDirectory>../outoutputDirectory>
                    configuration>
                plugin>
            plugins>
        build>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    配置

    bootstrap.yml
    spring:
      main:
        web-application-type: reactive
      application:
        name: ssc-cloud-gateway
      cloud:
        nacos:
          discovery:
            server-addr: 192.168.201.81:7777
            namespace: ssc-cloud-id
            group: DEV
            register-enabled: true
          config:
            enabled: off
    
        gateway:
          discovery:
            locator:
              enabled: true
          routes:
            - id : ssc-order-id               #路由编号(自定义)
    #          uri: https://www.bilibili.com/   #跳转地址(网页跳转),lb--负载调整(微服务)
              uri: lb://ssc-cloud-older
              predicates:
                - Path=/ssc-older/**       #url 的映射名称(让客户看到的)
              filters:
                - StripPrefix=1
            - id: ssc-paycenter-id               #路由编号(自定义)
              uri: lb://ssc-cloud-paycenter
              predicates:
                - Path=/ssc-pay/**
              filters:
                - StripPrefix=1
            - id: ssc-authority-id               #路由编号(自定义)
              uri: lb://ssc-cloud-authority
              predicates:
                - Path=/ssc-auth/**
              filters:
                - StripPrefix=1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    全局过滤器

    package com.wnhz.ssc.cloud.gateway.filter;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.wnhz.ssc.cloud.authority.feign.IAuthorityFeign;
    import com.wnhz.ssc.common.result.HttpResp;
    import com.wnhz.ssc.common.result.RespCode;
    import lombok.SneakyThrows;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.nio.charset.StandardCharsets;
    import java.util.List;
    import java.util.Objects;
    
    @Component
    @Slf4j
    public class SscGlobalGatewayFilter implements GlobalFilter, Ordered {
    
        @Lazy
        @Autowired
        private IAuthorityFeign authorityFeign;
    
        private final String LOGIN_TOKEN="ssc_login_token";
    
        @SneakyThrows
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();
            log.debug("我进入过滤中.....");
            ObjectMapper objectMapper = new ObjectMapper();
            DataBuffer dataBuffer = null;
    
            String requestURI =request.getURI().getPath() ;
            log.debug("请求uri:{}",requestURI);
    
           if(requestURI.endsWith("/authority/login")){//客户访问登录业务,直接跳转验证
               return chain.filter(exchange);
           }
    
           //1. Token不存在,客户未登录状态访问系统
            if(!isLogin(request)) {//如果客户没有登录
                log.debug("客户还没有登录,重定向到登录(baidu)页面");
                String redirectUrl = "https://www.baidu.com";
                log.debug("将客户请求重定向到指定页面: {}", redirectUrl);
                response.getHeaders().set(HttpHeaders.LOCATION, redirectUrl);
                //303状态码表示由于请求对应的资源存在着另一个URI,应使用GET方法定向获取请求的资源
                response.setStatusCode(HttpStatus.SEE_OTHER);
                response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
                return response.setComplete();
            }
            //2. token存在,调用验证微服务验证token是否有效
            String token = getToken(request);
            HttpResp block = authorityFeign.verifyToken(token).block();
            if(block.getCode()== RespCode.FAILED.getCode()){ //token验证失败
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                dataBuffer = response.bufferFactory().wrap(
                        objectMapper.writeValueAsString(
                                        block)
                                .getBytes(StandardCharsets.UTF_8));
                return response.writeWith(Mono.just(dataBuffer));
            }
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {
            return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
        }
    
        /**
         * 客户未登录,没有token
         * @param request
         * @return
         */
        private boolean isLogin(ServerHttpRequest request){
            HttpHeaders headers = request.getHeaders();
            log.debug("header===> {}", headers);
            List<String> loginToken = headers.get(LOGIN_TOKEN);
            if(Objects.nonNull(loginToken)){
                return true;
            }
            return false;
        }
    
        /**
         * 从请求中获取token
         * @param request
         */
        private String getToken(ServerHttpRequest request){
            HttpHeaders headers = request.getHeaders();
            log.debug("header===> {}", headers);
            List<String> tokens = headers.get(LOGIN_TOKEN);
            String token = tokens.get(0);
            return token;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111

    启动类

    package com.wnhz.ssc.cloud.gateway;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import reactivefeign.spring.config.EnableReactiveFeignClients;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableReactiveFeignClients(basePackages = "com.wnhz.ssc.cloud.authority.feign")
    public class GatewayApp {
        public static void main(String[] args) {
            SpringApplication.run(GatewayApp.class);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    HttpHeaders headers = request.getHeaders();
        log.debug("header===> {}", headers);
        List tokens = headers.get(LOGIN_TOKEN);
        String token = tokens.get(0);
        return token;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    }

    
    ### 启动类
    
    ```java
    package com.wnhz.ssc.cloud.gateway;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import reactivefeign.spring.config.EnableReactiveFeignClients;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableReactiveFeignClients(basePackages = "com.wnhz.ssc.cloud.authority.feign")
    public class GatewayApp {
        public static void main(String[] args) {
            SpringApplication.run(GatewayApp.class);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    化整为零优化重用,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang函数的定义和使用EP07
    C语言17---计算机的存储规则
    Go语言进阶,详解并发中的通道机制
    操作系统(Operating System)知识点复习——第九章 单处理器调度
    (二)笔记.net core学习之依赖注入、服务注册
    非关系型数据库NoSQL
    21、嵌套路由实战操作
    算法——前缀和之一维前缀和模版、二维前缀和模版、寻找数组中心下标
    简易实现通讯录3.0 (实现文件操作)
    495. Teemo Attacking
  • 原文地址:https://blog.csdn.net/qq_36115196/article/details/136195696