springcloud——gateway功能拓展

Posted tang_seven

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springcloud——gateway功能拓展相关的知识,希望对你有一定的参考价值。

目录

1.获取用户真实IP

2.统一跨域配置

3.redis令牌桶算法限流


1.获取用户真实IP

在我们的日常业务中,我们时常需要获取用户的IP地址,作登录日志、访问限制等相关操作。

而在我们的开发架构中,一般我们将服务分为多个微服务,然后使用一个统一的网关对他们进行路由控制管理:

如上图,我们可以看到,一般来说网关(一般使用ngnix或者springcloud gateway)会放在独立的一台服务器上,他的ip是不一样的。当用户请求发过来时,网关收到用户请求,然后根据路由匹配对应的微服务,使用feign调用对应的微服务,所以在微服务中获取的ip其实是网关的IP,而不是用户访问的真实IP。

所以,我们想要获取用户的真实IP有以下两个方法:

(1)在gateway中进行配置:

我们可以在springcloud gateway中的过滤器中,拦截用户请求,获取用户的真实ip后存入HTTP header中,再转发至微服务中。

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class CommonFilter implements GlobalFilter 

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) 
        ServerHttpRequest request = exchange.getRequest().mutate()
                //将获取的真实ip存入header微服务方便获取
                .header("X-Real-IP",exchange.getRequest().getRemoteAddress().getHostString())
                .build();
        return chain.filter(exchange.mutate().request(request).build());
    

上述代码中奖用户的请求IP作为key:X-Real-IP的值存储到header中,然后微服务中通过获取该header的方法即可获取到用户的真实IP。

String ip = request.getHeader("X-Real-IP");

注:X-Real-IP,一般只记录真实发出请求的客户端IP。该字段不是header中自带的,需要自行在网关中进行添加配置(如上述代码)。

(2)通过转发IP列表获取:

public class IpUtil 

    public static String getIpAddress(HttpServletRequest request) 
        //目前则是网关ip
        String ip = request.getHeader("X-Forwarded-For");
        if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) 
            int index = ip.indexOf(',');
            if (index != -1) 
                //只获取第一个值
                return ip.substring(0, index);
             else 
                return ip;
            
         else 
            //取不到真实ip则返回空,不能返回内网地址。
            return null;
        
    

X-Forwarded-For是用于记录代理信息的,每经过一级代理,该字段就会记录来源地址,经过多级代理,服务端就会记录每级代理的X-Forwarded-For信息,IP之间以“,”分隔开。 

所以,我们只要获取X-Forwarded-For中的第一个IP,就是用户的真实IP。

(3)测试

最后,我们可以在微服务的controller中编写代码测试一下,看看获取到的ip是怎样的:

    @GetMapping("/test")
    public Map<String,String> test(HttpServletRequest request)
        Map<String,String> map = new HashMap<>();
        map.put("真实ip",request.getHeader("X-Real-IP"));
        map.put("ip列表",request.getHeader("X-Forwarded-For"));
        map.put("转发ip",request.getRemoteAddr());
        return map;
    

 

可以看到,在微服务直接使用 request.getRemoteAddr()获取到的只是网关所在的地址(此处是一个内网地址),而不是真实IP。而我们通过网关配置,再使用request.getHeader("X-Real-IP")获取到的才是真实IP。request.getHeader("X-Forwarded-For")中获取到的IP列表中,由于只进行了springcloud gateway一次代理,只记录了第一次代理前的IP地址。其与真实IP是一致的,所以X-Forwarded-For中的第一个IP地址也是用户的真实IP地址。

2.统一跨域配置

跨域问题就是由于前端服务器和后端服务器的IP地址、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。

注意:跨域限制访问,其实是浏览器的限制

而在springcloud gateway中我们可以通过统一配置,对其访问的所有路由进行跨域统一处理:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class CorsConfig 

    @Bean
    public CorsWebFilter corsWebFilter() 
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        // 默认可不设置这个暴露的头。这个为了安全问题,不能使用*。
        // 设置成*,后面会报错:throw new IllegalArgumentException("'*' is not a valid exposed header value");
        corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL);
        corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
        corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsWebFilter(source);
    

3.redis令牌桶算法限流

由于网关会外界访问系统的统一入口,所以我们一般需要在网关对请求进行引流或者直接拒绝等操作,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机。

令牌桶算法:

令牌桶算法的原理是系统以恒定的速率产生令牌,然后把令牌放到令牌桶中(redis),令牌桶有一个容量,当令牌桶满了的时候,再向其中放令牌,那么多余的令牌会被丢弃;

当网关收到一个请求时,需要从令牌桶中取出一个令牌,如果此时令牌桶中没有令牌,那么则拒绝该请求。

springcloud gateway中为我们集成了基于redis的令牌桶算法,其实现方式十分简单:

server:
  port: 9527
  max-http-header-size: 102400
spring:
  application:
    name: cloud-gateway
    gateway: # 配置 Spring Cloud Gateway 相关属性
      discovery: # 配置网关发现机制
        locator: # 配置处理机制
          enabled: true # 开启网关自动映射处理逻辑
          lower-case-service-id: true # 开启服务名称小写转换。
      routes:  # 配置网关中的一个完整路由,包括命名,地址,谓词集合(规则),过滤器集合
        - id: user_student_routh # 路由定义的命名,唯一即可。
          uri: lb://cloud-student-manage # 当前路由定义对应的微服务转发地址,lb - 代表loadbalance
          predicates:
            - Path=/student/**   # 断言,路径相匹配的进行路由
          filters: # 配置过滤器集合
            - name: RequestRateLimiter
              args:
                keyResolver: '#@myKeyResolver'  # 使用SpringEL表达式,从Spring容器中找对象,并赋值。 '#@beanName',服务降级
                redis-rate-limiter.replenishRate: 100  # 生产令牌速度,每秒多少个令牌
                redis-rate-limiter.burstCapacity: 200  # 令牌桶容量
  redis:
    database: 0
    host: 127.0.0.1
    #redis默认端口
    port: 6379
    password:
    jedis:
      pool:
        max-active: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池中的最小空闲连接
        min-idle: 0
    # 连接超时时间(毫秒)
    timeout: 5000ms

只需要在application中配置对应的过滤器即可。

上述代码中还配置了当请求被拒绝时的服务降级相关配置,需要进行相关代码的编写:

(1)服务降级hystrix相关依赖

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

(2)服务降级配置:

@Component
public class MyKeyResolver implements KeyResolver 
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) 
        String remoteAddr = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
        return Mono.just(remoteAddr);
    

(3)服务降级接口:

@RestController
@Slf4j
public class AuthController 

    @RequestMapping(value="/downgrade")
    public CommonResult<Object> downgrade(Throwable e)
        return new CommonResult<>(444,"对不起,服务器繁忙,请稍后重试",e.getMessage());
    

#yyds干货盘点# springcloud整合gateway实现网关全局过滤器功能

springcloud整合gateway实现网关全局过滤器功能,在​​#yyds干货盘点# springcloud整合gateway实现网关 ​​基础功能上进行修改。

1.代码实现:

添加自定义全局过滤器

/**
* 自定义一个全局过滤器
* 实现 globalfilter , ordered接口
*/
@Component
public class LoginFilter implements GlobalFilter, Ordered

/**
* 执行过滤器中的业务逻辑
* 对请求参数中的access-token进行判断
* 如果存在此参数:代表已经认证成功
* 如果不存在此参数 : 认证失败.
* ServerWebExchange : 相当于请求和响应的上下文(zuul中的RequestContext)
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
System.out.println("执行了自定义的全局过滤器");
//1.获取请求参数access-token
String token = exchange.getRequest().getQueryParams().getFirst("access-token");
//2.判断是否存在
if(token == null)
//3.如果不存在 : 认证失败
System.out.println("没有登录");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete(); //请求结束

//4.如果存在,继续执行
return chain.filter(exchange); //继续向下执行


/**
* 指定过滤器的执行顺序 , 返回值越小,执行优先级越高
*/
@Override
public int getOrder()
return 0;

2.实现效果:

 启动nacos,再启动项目gateway-service, gateway-client。打开浏览器访问

​http://localhost:8001/client/index​

#yyds干货盘点#

访问​​http://localhost:8001/client/index?access-token=token​

实现了添加token后就可以访问。

#yyds干货盘点#

以上是关于springcloud——gateway功能拓展的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点# springcloud整合gateway实现网关全局过滤器功能

SpringCloud系列之网关gateway-4.路由功能详解

#私藏项目实操分享#SpringCloud技术专题「Gateway网关系列」微服务网关服务的Gateway功能配置指南分析

springcloud gateway 限制文件上传大小

spring-cloud-gateway之GatewayFilterFactory

SpringCloud系列之网关gateway-9.自定义过滤器实现接口计时功能