springcloudgateway拦截器例子

springcloudgateway写2个filter,一个GlobalFilter实现公共token的校验,一个实现具体应用的拦截器逻辑。

package com.example.gate.filter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
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.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.synchronoss.cloud.nio.multipart.util.IOUtils;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class TokenFilter implements GlobalFilter, Ordered {

	Logger logger = LoggerFactory.getLogger(TokenFilter.class);

	@Override
	public int getOrder() {
		// TODO Auto-generated method stub
		return 120;
	}
	
	@Override
	public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		logger.info("TokenFilter开始............");
		
		getAllParamtersRequest(exchange.getRequest());
		getAllHeadersRequest(exchange.getRequest());
		
		
		//拦截的逻辑。根据具体业务逻辑做拦截。
		String token = exchange.getRequest().getQueryParams().getFirst("token");
		if (token == null || token.isEmpty()) {
			logger.info("token is empty...");
//			exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//			return exchange.getResponse().setComplete();
			
			//设置status和body
	        return Mono.defer(() -> {
	            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//设置status
	            final ServerHttpResponse response = exchange.getResponse();
	            byte[] bytes = "{\"code\":\"99999\",\"message\":\"非法访问,没有检测到token~~~~~~\"}".getBytes(StandardCharsets.UTF_8);
	            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
	            response.getHeaders().set("aaa", "bbb");//设置header
	            logger.info("TokenFilter拦截非法请求,没有检测到token............");
	            return response.writeWith(Flux.just(buffer));//设置body
	        });
		}
		
		//没有被if条件拦截,就放行
		return chain.filter(exchange);
	}
	
	
	private Map getAllParamtersRequest(ServerHttpRequest request) {
		logger.info("getAllParamtersRequest开始............");
		Map map = new HashMap();
		MultiValueMap paramNames = request.getQueryParams();
		Iterator it= paramNames.keySet().iterator();
		while (it.hasNext()) {
			String paramName = (String) it.next();
			
			List paramValues = paramNames.get(paramName);
			if (paramValues.size() >= 1) {
				String paramValue = paramValues.get(0);
				logger.info("request参数:"+paramName+",值:"+paramValue);
				map.put(paramName, paramValue);
			}
		}
		return map;
	}
	
	private Map getAllHeadersRequest(ServerHttpRequest request) {
		logger.info("getAllHeadersRequest开始............");
		Map map = new HashMap();
		HttpHeaders hearders = request.getHeaders();
		Iterator it= hearders.keySet().iterator();
		while (it.hasNext()) {
			String keyName = (String) it.next();
			
			List headValues = hearders.get(keyName);
			if (headValues.size() >= 1) {
				String kvalue = headValues.get(0);
				logger.info("request header的key:"+keyName+",值:"+kvalue);
				map.put(keyName, kvalue);
			}
		}
		return map;
	}
	
//	/**
//	 * 
//	 * @param request
//	 * @return
//	 */
//	private Map getAllParamtersRequest(HttpServletRequest request) {
//		logger.info("getAllParamtersRequest............");
//		Map map = new HashMap();
//		Enumeration paramNames = request.getParameterNames();
//		while (paramNames.hasMoreElements()) {
//			String paramName = (String) paramNames.nextElement();
//			String[] paramValues = request.getParameterValues(paramName);
//			if (paramValues.length >= 1) {
//				String paramValue = paramValues[0];
//				if (paramValue.length() != 0) {
//					logger.info("request参数:"+paramName+",值:"+paramValue);
//					map.put(paramName, paramValue);
//				}
//
//			}
//		}
//		return map;
//	}

}
package com.example.gate.filter;

import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 
 * Ordered 负责filter的顺序,数字越小越优先,越靠前。
 * 
 * GatewayFilter:
 * 需要通过spring.cloud.routes.filters 配置在具体路由下,
 * 只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。
 * 需要用代码的形式,配置一个RouteLocator,里面写路由的配置信息。
 * 
 * GlobalFilter:
 * 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,
 * 它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
 * 代码配置需要声明一个GlobalFilter对象。
 * 
 * 
 * 对一个应用来说,GatewayFilter和GlobalFilter是等价的,order也会按照顺序进行拦截。所以两个order不要写一样!
 * 
 * @author lsy
 *
 */
public class UrlFilter implements GatewayFilter, Ordered {

	Logger logger = LoggerFactory.getLogger(UrlFilter.class);
	
	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		logger.info("UrlFilter开始............");
		URI uri = exchange.getRequest().getURI();
		logger.info("UrlFilter开始............URI==="+uri.toString());
		
		HttpMethod method = exchange.getRequest().getMethod();
		logger.info("UrlFilter开始............HttpMethod==="+method.toString());
		
		String methodValue= exchange.getRequest().getMethodValue();
		logger.info("UrlFilter开始............getMethodValue==="+methodValue);
		
		
		RequestPath requestPath =exchange.getRequest().getPath();
		logger.info("UrlFilter开始............RequestPath==="+requestPath.toString());
		
		InetSocketAddress inetSocketAddress =exchange.getRequest().getRemoteAddress();
		logger.info("UrlFilter开始............InetSocketAddress==="+inetSocketAddress.toString());
		
		
		//拦截的逻辑。根据具体业务逻辑做拦截。
		String perm = exchange.getRequest().getQueryParams().getFirst("perm");
		if (perm == null || perm.isEmpty()) {
			logger.info("perm is empty...");
			//设置HttpStatus,返回体是空的
//					exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//					return exchange.getResponse().setComplete();
			
			//下面可以设置返回体的具体内容
			//设置status和body
	        return Mono.defer(() -> {
	            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//设置status
	            final ServerHttpResponse response = exchange.getResponse();
	            byte[] bytes = "{\"code\":\"99000\",\"message\":\"非法访问,没有检测到权限码perm~~~~~~\"}".getBytes(StandardCharsets.UTF_8);
	            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
	            response.getHeaders().set("permValid", "false");//设置header
	            logger.info("UrlFilter拦截非法请求,没有检测到权限码perm............");
	            return response.writeWith(Flux.just(buffer));//设置body
	        });
		}
		
		//没有被if条件拦截,就放行
		return chain.filter(exchange);
	}

}
package com.example.gate.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.example.gate.filter.TokenFilter;
import com.example.gate.filter.UrlFilter;

@Configuration
public class FilterConfig {

	Logger logger = LoggerFactory.getLogger(FilterConfig.class);
	
	@Bean
	public GlobalFilter tokenFilter() {
	    return new TokenFilter();
	}
	
	
	
	
	@Bean
    public RouteLocator urlFilterRouteLocator(RouteLocatorBuilder builder) {
		logger.info("FilterConfig---urlFilterRouteLocator---");
        return builder.routes()
                .route(r -> r.path("/one/**")
                        .filters(f -> f.stripPrefix (1).filter(new UrlFilter())
                                .addResponseHeader("urlFilterFlag", "pass"))
                        .uri("lb://one")
                        .order(0)
                        .id("one")
                )
                .build();
    }
	
	
	
}
server: 
   port: 8888
   
spring:
  application:
    name: gate
  cloud:
    consul:
      host: 127.0.0.1  #注册中心的ip或host。也是集群地址,配置一个即可。
      port: 8500
      discovery:
        #enabled: true #使用服务发现客户端定义路由信息。即不配置routes节点,采用默认配置;配置了,以配置为准。默认true。
        register: true #是否将服务注册到Consul集群中心
        deregister: true #默认true,服务停止时注销服务,即从服务列表中删除。设置成false的话,???
        #service-name: ${spring.application.name}  #注册在consul上面的名字,在consul的调用中,是通过此名字调用的。默认服务名,不要改
        instance-id: ${spring.application.name}-${spring.cloud.client.ip-address}:${server.port} #只供显示用,在ID列显示
        health-check-interval: 5s  #配置 Consul 健康检查频率,也就是心跳频率。
        health-check-critical-timeout: 1s #健康检查失败多长时间后,取消注册。在node上显示红色叉。
        #health-check-path: /tmp #健康检查路径
        #prefer-ip-address: true #表示注册时使用IP而不是hostname
    gateway:
      discovery:
        locator:
          enabled: true #表明gateway开启服务注册和发现的功能,并且自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
          lower-case-service-id: true # 服务名小写
#      routes:
#      - id: one
#        # lb代表从注册中心获取服务,且已负载均衡方式转发
#        uri: lb://one #目标服务地址
#        predicates: #路由条件
#          - Path=/one/**
#        #过滤规则。 StripPrefix=1标识去掉url的第一个前缀,用后面的内容拼接到uri后面
#        filters:
#          - StripPrefix=1
#      - id: two
#        # lb代表从注册中心获取服务,且已负载均衡方式转发
#        uri: lb://two #目标服务地址
#        predicates: #路由条件
#          - Path=/two/**
#        #过滤规则。 StripPrefix=1标识去掉url的第一个前缀,用后面的内容拼接到uri后面
#        filters:
#          - StripPrefix=1
        

#eureka:
#  instance:
#    prefer-ip-address: true
#    instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
#    lease-expiration-duration-in-seconds: 10
#    lease-renewal-interval-in-seconds: 3
#  client:
#    registry-fetch-interval-seconds: 5
#    serviceUrl:
#      defaultZone: http://localhost:8761/eureka/

#启用监控.开启监控后,可直接通过actuator的rest接口查看服务的详细信息。例如查看网关的路由:http://localhost:8888/actuator/gateway/routes
management:
  endpoints:
    web:
      exposure:
        include: 
        - "*"  # 开放所有端点health,info,metrics,通过actuator/+端点名就可以获取相应的信息。默认打开health和info
  endpoint:
    health:
      show-details: always  #未开启actuator/health时,我们获取到的信息是{"status":"UP"},status的值还有可能是 DOWN。开启后打印详细信息



  

我起2个微服务,一个叫one,一个叫two。都有一个测试的controller:

package com.example.one.controller;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

//@Component
@RestController
//@RequestMapping("/beat")
public class HelloController {
	
	@Value("${spring.cloud.client.ip-address}")
	private String ip;
	
	@Value("${spring.application.name}")
	private String servername;
	
	@Value("${server.port}")
	private String port;
	
//	@Autowired
//	private ComConfig conf;

	@GetMapping(value="/hello")
	public String hello() {
		String[] colorArr=new String[] {"red","blue","green","pink","gray"};
		DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//        LocalDateTime startTime = LocalDateTime.now();
		String message="hello !  I am   ["+servername+":"+ip+":"+port+"]"+"..."+LocalDateTime.now().format(df);
		System.out.println(message);
		return message;
	}
}

根据以上代码,我定义了一个全局的过滤器TokenFilter,给one服务添加了UrlFilter(two没有)

经测试,全局拦截器对2个应用都起作用。UrlFilter只对one起作用。

值得注意的是,对于one,他有2个Filter。顺序是按照order来得。对于他来说已经不分GatewayFilter和GlobalFilter了。

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(springcloud)